LCOV - code coverage report
Current view: top level - tools - TypesafePtr.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 123 132 93.2 %
Date: 2024-10-18 14:00:25 Functions: 44 60 73.3 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2018-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #ifndef __PLUMED_tools_TypesafePtr_h
      23             : #define __PLUMED_tools_TypesafePtr_h
      24             : 
      25             : #include "Exception.h"
      26             : 
      27             : #include <memory>
      28             : #include <iosfwd>
      29             : #include <map>
      30             : #include <utility>
      31             : #include <mutex>
      32             : #include <cstdio>
      33             : #include <array>
      34             : #include <cstring>
      35             : #include <type_traits>
      36             : #include <climits>
      37             : #include <limits>
      38             : #include <initializer_list>
      39             : #include <algorithm>
      40             : 
      41             : namespace PLMD {
      42             : 
      43     1825757 : static inline bool typesafePtrSkipCheck() {
      44     1825757 :   static const bool ret=std::getenv("PLUMED_TYPESAFE_IGNORE");
      45     1825757 :   return ret;
      46             : }
      47             : 
      48             : template<class T>
      49     1819264 : std::size_t typesafePtrSizeof() {
      50     1819264 :   return sizeof(T);
      51             : }
      52             : 
      53             : template<>
      54             : inline
      55             : std::size_t typesafePtrSizeof<void>() {
      56             :   return 0;
      57             : }
      58             : 
      59             : template<>
      60             : inline
      61             : std::size_t typesafePtrSizeof<const void>() {
      62             :   return 0;
      63             : }
      64             : 
      65             : /**
      66             : \ingroup TOOLBOX
      67             : Class to deal with propoagation of typesafe pointers.
      68             : 
      69             : */
      70             : class TypesafePtr {
      71             :   /// Small structure used to pass elements of a shape initializer_list
      72             :   struct SizeLike {
      73             :     std::size_t size;
      74             :     SizeLike(short unsigned size): size(size) {}
      75      655156 :     SizeLike(unsigned size): size(size) {}
      76         988 :     SizeLike(long unsigned size): size(size) {}
      77             :     SizeLike(long long unsigned size): size(size) {}
      78             :     SizeLike(short size): size(std::size_t(size)) {}
      79        5279 :     SizeLike(int size): size(std::size_t(size)) {}
      80             :     SizeLike(long int size): size(std::size_t(size)) {}
      81             :     SizeLike(long long int size): size(std::size_t(size)) {}
      82             :   };
      83             : 
      84     1425342 :   inline void init_shape(const std::size_t* shape) {
      85     1425342 :     this->shape[0]=0;
      86     1425342 :     if(shape && *shape>0) {
      87             :       std::size_t nelem_=1;
      88             :       unsigned i=0;
      89     1046841 :       for(i=0; i<this->shape.size(); i++) {
      90     1046841 :         this->shape[i]=*shape;
      91     1046841 :         if(*shape==0) break;
      92      652544 :         nelem_*=*shape;
      93      652544 :         shape++;
      94             :       }
      95      394297 :       plumed_assert(i<this->shape.size()); // check that last element is actually zero
      96      394297 :       if(nelem==0) nelem=nelem_;
      97      394297 :       plumed_assert(nelem==nelem_) << "Inconsistent shape/nelem";
      98             :     }
      99     1425342 :   }
     100             : 
     101             :   static std::string extra_msg();
     102             : 
     103             : public:
     104             : 
     105             :   TypesafePtr(void* ptr, std::size_t nelem, const std::size_t* shape, std::size_t flags):
     106      408496 :     ptr(ptr),
     107      408496 :     nelem(nelem),
     108      408496 :     flags(flags)
     109             :   {
     110      408496 :     buffer[0]='\0';
     111      408496 :     init_shape(shape);
     112             :   }
     113             : 
     114             :   static const unsigned maxrank=4;
     115             :   static TypesafePtr fromSafePtr(void* safe);
     116             :   static TypesafePtr setNelemAndShape(const TypesafePtr &other, std::size_t nelem, const std::size_t* shape) {
     117      388002 :     return TypesafePtr(other.ptr,nelem,shape,other.flags);
     118             :   }
     119             :   static TypesafePtr unchecked(const void* ptr) {
     120             :     return TypesafePtr(const_cast<void*>(ptr),0,nullptr,0);
     121             :   }
     122             :   static constexpr unsigned short is_integral=3;
     123             :   static constexpr unsigned short is_floating_point=4;
     124             :   static constexpr unsigned short is_file=5;
     125             : 
     126     1802698 :   TypesafePtr() {
     127     1802698 :     shape[0]=0;
     128     1802698 :     buffer[0]='\0';
     129             :   }
     130             : 
     131             :   TypesafePtr(std::nullptr_t)
     132      294510 :   {
     133      294510 :     shape[0]=0;
     134      287554 :     buffer[0]='\0';
     135             :   }
     136             : 
     137             : /// Macro that generate a constructor with given type and flags
     138             : #define __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type,type_,flags_) \
     139             :   TypesafePtr(type_*ptr, std::size_t nelem=0, const std::size_t* shape=nullptr) : \
     140             :     ptr((void*)const_cast<type*>(ptr)), \
     141             :     nelem(nelem), \
     142             :     flags(flags_) \
     143             :   { \
     144             :     init_shape(shape); \
     145             :     buffer[0]='\0'; \
     146             :   }
     147             : 
     148             : /// Macro that uses __PLUMED_WRAPPER_TYPESAFEPTR_INNER to generate constructors with
     149             : /// all possible pointer-const combinations
     150             : #define __PLUMED_WRAPPER_TYPESAFEPTR(type, code,size) \
     151             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type,             size | (0x10000*(code)) | (0x2000000*2)) \
     152             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type const,       size | (0x10000*(code)) | (0x2000000*3)) \
     153             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*,            size | (0x10000*(code)) | (0x2000000*4)) \
     154             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*const,       size | (0x10000*(code)) | (0x2000000*5)) \
     155             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*,      size | (0x10000*(code)) | (0x2000000*6)) \
     156             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*const, size | (0x10000*(code)) | (0x2000000*7))
     157             : 
     158             : /// Macro that generates the constructors from empy types (those of which sizeof cannot be computed)
     159             : #define __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(type,code) __PLUMED_WRAPPER_TYPESAFEPTR(type,code,0)
     160             : 
     161             : /// Macro that generates the constructors from sized types (those of which sizeof can be computed).
     162             : /// In addition to generating constructors with all pointer types, it generates a constructor to
     163             : /// allow pass-by-value
     164             : #define __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(type,code) \
     165             :   __PLUMED_WRAPPER_TYPESAFEPTR(type,code,sizeof(type)) \
     166             :   TypesafePtr(type val, std::size_t nelem=0, const std::size_t* shape=nullptr): \
     167             :       nelem(1), \
     168             :       flags(sizeof(type) | (0x10000*(code)) | (0x2000000*1)) \
     169             :     { \
     170             :     plumed_assert(sizeof(type)<=32); \
     171             :     ptr=&buffer[0]; \
     172             :     flags=sizeof(type) | (0x10000*(code)) | (0x2000000*1); \
     173             :     std::memcpy(&buffer[0],&val,sizeof(type)); \
     174             :     init_shape(shape); \
     175             :   }
     176             : 
     177             : /// Here we create all the required instances
     178             : /// 1: void
     179             : /// 3: integral
     180             : /// 4: floating
     181             : /// 5: FILE
     182             : /// 0x100: unsigned
     183        1231 :   __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(void,1)
     184        2151 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(char,(CHAR_MIN==0)*0x100+3)
     185             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(signed char,3)
     186             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned char,0x100+3)
     187             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(short,3)
     188             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned short,0x100+3)
     189      286468 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(int,3)
     190         102 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned int,0x100+3)
     191          94 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long,3)
     192             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long,0x100+3)
     193      265012 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long long,3)
     194        3939 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long long,0x100+3)
     195           0 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(float,4)
     196      328385 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(double,4)
     197             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long double,4)
     198         996 :   __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(FILE,5)
     199             : 
     200             :   ~TypesafePtr() {
     201     3099926 :   }
     202             : 
     203             : 
     204             :   TypesafePtr(const TypesafePtr&other) = delete;
     205             : 
     206             :   TypesafePtr & operator=(const TypesafePtr & other) = delete;
     207             : 
     208             :   TypesafePtr(TypesafePtr&&other):
     209             :     buffer(other.buffer),
     210             :     ptr(other.ptr==&other.buffer[0] ? &buffer[0] : other.ptr),
     211             :     nelem(other.nelem),
     212             :     shape(other.shape),
     213             :     flags(other.flags)
     214             :   {
     215             :     other.ptr=nullptr;
     216             :   }
     217             : 
     218             :   TypesafePtr copy() const;
     219             : 
     220      996000 :   TypesafePtr & operator=(TypesafePtr && other) {
     221      996000 :     ptr=(other.ptr==&other.buffer[0] ? &buffer[0] : other.ptr);
     222      996000 :     flags=other.flags;
     223      996000 :     nelem=other.nelem;
     224      996000 :     shape=other.shape;
     225      996000 :     buffer=other.buffer;
     226      996000 :     other.ptr=nullptr;
     227      996000 :     return *this;
     228             :   }
     229             : 
     230           7 :   std::string type_str() const {
     231           7 :     auto type=(flags>>16)&0xff;
     232           7 :     if(type==0) return "wildcard";
     233           2 :     if(type==1) return "void";
     234           0 :     if(type==2) return "integral";
     235           0 :     if(type==3) return "integral";
     236           5 :     if(type==4) return "floating point";
     237           0 :     if(type==5) return "FILE";
     238           0 :     return "unknown";
     239             :   }
     240             : 
     241             : private:
     242             : 
     243             :   template<typename T>
     244     1822595 :   T* get_priv(std::size_t nelem, const std::size_t* shape, bool byvalue) const {
     245             : 
     246     1822595 :     if(typesafePtrSkipCheck()) return (T*) ptr;
     247             :     typedef typename std::remove_pointer<T>::type T_noptr;
     248     1822595 :     if(flags==0) return (T*) ptr; // no check
     249     1820279 :     auto size=flags&0xffff;
     250     1820279 :     auto type=(flags>>16)&0xff;
     251             :     // auto unsi=(flags>>24)&0x1; // ignored
     252     1822022 :     auto cons=(flags>>25)&0x7;
     253             : 
     254             :     // type=0: ignore check
     255             :     // type>5: undefined yet
     256     1820279 :     if(type!=0 && type<=5) {
     257      575293 :       if(std::is_integral<T_noptr>::value && (type!=is_integral)) {
     258          24 :         throw ExceptionTypeError() <<"This command expects an integer type. Received a " << type_str() << " instead"<<extra_msg();
     259             :       }
     260     1243972 :       if(std::is_floating_point<T_noptr>::value && (type!=is_floating_point)) {
     261           4 :         throw ExceptionTypeError() <<"This command expects a floating point type. Received a " << type_str() << " instead"<<extra_msg();
     262             :       }
     263        1013 :       if(std::is_same<FILE,typename std::remove_const<T_noptr>::type>::value && (type!=is_file)) {
     264           0 :         throw ExceptionTypeError() <<"This command expects a FILE. Received a " << type_str() << " instead"<<extra_msg();
     265             :       }
     266             :     }
     267             : 
     268     1820272 :     if(size>0 && typesafePtrSizeof<T_noptr>() >0 && typesafePtrSizeof<T_noptr>()!=size) {
     269          24 :       throw ExceptionTypeError() << "This command expects a type with size " << typesafePtrSizeof<T_noptr>() << ". Received type has size " << size << " instead"<<extra_msg();
     270             :     }
     271             : 
     272     1822009 :     if(!byvalue) if(cons==1) {
     273           3 :         throw ExceptionTypeError() << "This command is trying to take the address of an argument that was passed by value"<<extra_msg();
     274             :       }
     275             : 
     276             :     // cons==1 (by value) is here treated as cons==3 (const type*)
     277     1816729 :     if(cons>0) {
     278             :       if(!std::is_pointer<T>::value) {
     279             :         if(std::is_void<T>::value) {
     280        1743 :           if(cons==1) {
     281           0 :             throw ExceptionTypeError() << "This command expects a void pointer. It received a value instead"<<extra_msg();
     282             :           }
     283             :         } else {
     284     1814870 :           if(cons!=1 && cons!=2 && cons!=3) {
     285           0 :             throw ExceptionTypeError() << "This command expects a pointer or a value. It received a pointer-to-pointer instead"<<extra_msg();
     286             :           }
     287             :         }
     288             :         if(!std::is_const<T>::value) {
     289      757536 :           if(cons==3) {
     290           6 :             throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a non modifiable pointer instead (const T*)"<<extra_msg();
     291      757534 :           } else if(cons==1) {
     292           0 :             throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a value instead (T)"<<extra_msg();
     293             :           }
     294             :         }
     295             :       } else {
     296             :         if(!std::is_const<T>::value) {
     297         116 :           if(cons==1) throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a value intead"<<extra_msg();
     298         116 :           if(cons==2 || cons==3) throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a pointer intead"<<extra_msg();
     299             :           if(!std::is_const<T_noptr>::value) {
     300             :             if(cons!=4) throw ExceptionTypeError() << "This command expects a modifiable pointer-to-pointer (T**)"<<extra_msg();
     301             :           } else {
     302         116 :             if(cons!=6) throw ExceptionTypeError() << "This command expects a modifiable pointer to unmodifiable pointer (const T**)"<<extra_msg();
     303             :           }
     304             :         } else {
     305             :           if(!std::is_const<T_noptr>::value) {
     306             :             if(cons!=4 && cons!=5) throw ExceptionTypeError() << "This command expects T*const* pointer, and can only receive T**  or T*const* pointers"<<extra_msg();
     307             :           }
     308             :         }
     309             :       }
     310             :     }
     311             :     // check full shape, if possible
     312     1822006 :     if(shape && shape[0] && this->shape[0]) {
     313     1298766 :       for(unsigned i=0; i<this->shape.size(); i++) {
     314     1298766 :         if(shape[i]==0 && this->shape[i]!=0) {
     315          12 :           throw ExceptionTypeError() << "Incorrect number of axis (passed greater than requested)"<<extra_msg();
     316             :         }
     317     1298762 :         if(shape[i]!=0 && this->shape[i]==0) {
     318          12 :           throw ExceptionTypeError() << "Incorrect number of axis (requested greater than passed)"<<extra_msg();
     319             :         }
     320     1298758 :         if(shape[i]==0) break;
     321      863216 :         if((shape[i]>this->shape[i])) {
     322          80 :           throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << this->shape[i] << " have been passed"<<extra_msg();
     323             :         }
     324      863196 :         if(i>0 && (shape[i]<this->shape[i])) {
     325           8 :           throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << this->shape[i] << " have been passed"<<extra_msg();
     326             :         }
     327             :       }
     328             :     }
     329     1821976 :     if(nelem==0 && shape && shape[0]>0) {
     330      528100 :       nelem=1;
     331     1517559 :       for(unsigned i=0; i<this->shape.size(); i++) {
     332     1517559 :         if(shape[i]==0) break;
     333      989459 :         nelem*=shape[i];
     334             :       }
     335             :     }
     336             :     // check number of elements
     337     1821976 :     if(nelem>0 && this->nelem>0) if(!(nelem<=this->nelem)) {
     338           8 :         throw ExceptionTypeError() << "This command wants to access " << nelem << " from this pointer, but only " << this->nelem << " have been passed"<<extra_msg();
     339             :       }
     340     1821974 :     return (T*) ptr;
     341             :   }
     342             : 
     343             : public:
     344             : 
     345             :   template<typename T>
     346       13120 :   void set(T val) const {
     347       13120 :     *get_priv<T>(0,nullptr,false)=val;
     348       13120 :   }
     349             : 
     350             :   template<typename T>
     351      839271 :   typename std::enable_if<std::is_pointer<T>::value,T>::type get() const {
     352             :     typedef typename std::remove_pointer<T>::type T_noptr;
     353      842886 :     return get_priv<T_noptr>(0,nullptr,false);
     354             :   }
     355             : 
     356             :   template<typename T>
     357      302004 :   typename std::enable_if<!std::is_pointer<T>::value,T>::type get() const {
     358      302004 :     return *get_priv<const T>(1,nullptr,true);
     359             :   }
     360             : 
     361             :   // this will make sure that a null character is present
     362        3162 :   const char* getCString() const {
     363        3162 :     const char* ptr=get_priv<const char>(0,nullptr,false);
     364        3162 :     if(!typesafePtrSkipCheck() && this->nelem>0 && this->nelem<std::numeric_limits<std::size_t>::max()) {
     365             :       std::size_t i=0;
     366       18736 :       for(; i<this->nelem; i++) if(ptr[i]==0) break;
     367         615 :       if(i==this->nelem) throw ExceptionTypeError() << "PLUMED is expecting a null terminated string, but no null character was found";
     368             :     }
     369        3160 :     return ptr;
     370             :   }
     371             : 
     372             :   template<typename T,typename I>
     373             :   typename std::enable_if<std::is_integral<I>::value, T>::type get(I nelem) const {
     374             :     static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
     375             :     typedef typename std::remove_pointer<T>::type T_noptr;
     376             :     return get_priv<T_noptr>(std::size_t(nelem),nullptr,false);
     377             :   }
     378             : 
     379             :   template<typename T>
     380      661423 :   T get(std::initializer_list<SizeLike> shape) const {
     381             :     static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
     382      661423 :     plumed_assert(shape.size()<=maxrank);
     383             :     std::array<std::size_t,maxrank+1> shape_;
     384             :     typedef typename std::remove_pointer<T>::type T_noptr;
     385             :     unsigned j=0;
     386     1917343 :     for(auto i : shape) {
     387     1255920 :       shape_[j]=i.size;
     388     1255920 :       j++;
     389             :     }
     390      661423 :     shape_[j]=0;
     391      661423 :     return get_priv<T_noptr>(0,&shape_[0],false);
     392             :   }
     393             : 
     394             :   operator bool() const noexcept {
     395      728866 :     return ptr;
     396             :   }
     397             : 
     398             :   void* getRaw() const noexcept {
     399         222 :     return ptr;
     400             :   }
     401             : 
     402             :   std::size_t getNelem() const noexcept {
     403         222 :     return nelem;
     404             :   }
     405             : 
     406             :   const std::size_t* getShape() const noexcept {
     407             :     return shape.data();
     408             :   }
     409             : 
     410             :   std::size_t getFlags() const noexcept {
     411         222 :     return flags;
     412             :   }
     413             : 
     414             : private:
     415             :   std::array<char,32> buffer;
     416             :   void* ptr=nullptr;
     417             :   std::size_t nelem=0;
     418             :   std::array<std::size_t,maxrank+1> shape; // make sure to initialize this!
     419             :   std::size_t flags=0;
     420             : };
     421             : 
     422             : }
     423             : 
     424             : 
     425             : #endif

Generated by: LCOV version 1.16