LCOV - code coverage report
Current view: top level - tools - TypesafePtr.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 114 130 87.7 %
Date: 2024-10-11 08:09:47 Functions: 40 52 76.9 %

          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 <initializer_list>
      38             : 
      39             : namespace PLMD {
      40             : 
      41     1004481 : static inline bool typesafePtrSkipCheck() {
      42     1004481 :   static const bool ret=std::getenv("PLUMED_TYPESAFE_IGNORE");
      43     1004481 :   return ret;
      44             : }
      45             : 
      46             : template<class T>
      47      997365 : std::size_t typesafePtrSizeof() {
      48      997365 :   return sizeof(T);
      49             : }
      50             : 
      51             : template<>
      52             : inline
      53             : std::size_t typesafePtrSizeof<void>() {
      54             :   return 0;
      55             : }
      56             : 
      57             : template<>
      58             : inline
      59             : std::size_t typesafePtrSizeof<const void>() {
      60             :   return 0;
      61             : }
      62             : 
      63             : /**
      64             : \ingroup TOOLBOX
      65             : Class to deal with propoagation of typesafe pointers.
      66             : 
      67             : */
      68             : class TypesafePtr {
      69     1015683 :   inline void init_shape(const std::size_t* shape) {
      70     1015683 :     this->shape[0]=0;
      71     1015683 :     if(shape && *shape>0) {
      72             :       std::size_t nelem_=1;
      73             :       unsigned i=0;
      74         812 :       for(i=0; i<this->shape.size(); i++) {
      75         812 :         this->shape[i]=*shape;
      76         812 :         if(*shape==0) break;
      77         464 :         nelem_*=*shape;
      78         464 :         shape++;
      79             :       }
      80         348 :       plumed_assert(i<this->shape.size()); // check that last element is actually zero
      81         348 :       if(nelem==0) nelem=nelem_;
      82         348 :       plumed_assert(nelem==nelem_) << "Inconsistent shape/nelem";
      83             :     }
      84     1015683 :   }
      85             : 
      86             :   static std::string extra_msg();
      87             : 
      88             : public:
      89             : 
      90             :   TypesafePtr(void* ptr, std::size_t nelem, const std::size_t* shape, std::size_t flags):
      91      239070 :     ptr(ptr),
      92      239070 :     nelem(nelem),
      93      239070 :     flags(flags)
      94             :   {
      95      239070 :     buffer[0]='\0';
      96      239070 :     init_shape(shape);
      97             :   }
      98             : 
      99             :   static const unsigned maxrank=4;
     100             :   static TypesafePtr fromSafePtr(void* safe);
     101             :   static TypesafePtr setNelemAndShape(const TypesafePtr &other, std::size_t nelem, const std::size_t* shape) {
     102      226212 :     return TypesafePtr(other.ptr,nelem,shape,other.flags);
     103             :   }
     104             :   static TypesafePtr unchecked(const void* ptr) {
     105             :     return TypesafePtr(const_cast<void*>(ptr),0,nullptr,0);
     106             :   }
     107             :   static constexpr unsigned short is_integral=3;
     108             :   static constexpr unsigned short is_floating_point=4;
     109             :   static constexpr unsigned short is_file=5;
     110             : 
     111     1195936 :   TypesafePtr() {
     112     1195936 :     shape[0]=0;
     113     1006040 :     buffer[0]='\0';
     114             :   }
     115             : 
     116             :   TypesafePtr(std::nullptr_t)
     117      252074 :   {
     118      252074 :     shape[0]=0;
     119      251050 :     buffer[0]='\0';
     120             :   }
     121             : 
     122             : /// Macro that generate a constructor with given type and flags
     123             : #define __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type,type_,flags_) \
     124             :   TypesafePtr(type_*ptr, std::size_t nelem=0, const std::size_t* shape=nullptr) : \
     125             :     ptr((void*)const_cast<type*>(ptr)), \
     126             :     nelem(nelem), \
     127             :     flags(flags_) \
     128             :   { \
     129             :     init_shape(shape); \
     130             :     buffer[0]='\0'; \
     131             :   }
     132             : 
     133             : /// Macro that uses __PLUMED_WRAPPER_TYPESAFEPTR_INNER to generate constructors with
     134             : /// all possible pointer-const combinations
     135             : #define __PLUMED_WRAPPER_TYPESAFEPTR(type, code,size) \
     136             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type,             size | (0x10000*(code)) | (0x2000000*2)) \
     137             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type const,       size | (0x10000*(code)) | (0x2000000*3)) \
     138             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*,            size | (0x10000*(code)) | (0x2000000*4)) \
     139             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*const,       size | (0x10000*(code)) | (0x2000000*5)) \
     140             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*,      size | (0x10000*(code)) | (0x2000000*6)) \
     141             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*const, size | (0x10000*(code)) | (0x2000000*7))
     142             : 
     143             : /// Macro that generates the constructors from empy types (those of which sizeof cannot be computed)
     144             : #define __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(type,code) __PLUMED_WRAPPER_TYPESAFEPTR(type,code,0)
     145             : 
     146             : /// Macro that generates the constructors from sized types (those of which sizeof can be computed).
     147             : /// In addition to generating constructors with all pointer types, it generates a constructor to
     148             : /// allow pass-by-value
     149             : #define __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(type,code) \
     150             :   __PLUMED_WRAPPER_TYPESAFEPTR(type,code,sizeof(type)) \
     151             :   TypesafePtr(type val, std::size_t nelem=0, const std::size_t* shape=nullptr): \
     152             :       nelem(1), \
     153             :       flags(sizeof(type) | (0x10000*(code)) | (0x2000000*1)) \
     154             :     { \
     155             :     plumed_assert(sizeof(type)<=32); \
     156             :     ptr=&buffer[0]; \
     157             :     flags=sizeof(type) | (0x10000*(code)) | (0x2000000*1); \
     158             :     std::memcpy(&buffer[0],&val,sizeof(type)); \
     159             :     init_shape(shape); \
     160             :   }
     161             : 
     162             : /// Here we create all the required instances
     163             : /// 1: void
     164             : /// 3: integral
     165             : /// 4: floating
     166             : /// 5: FILE
     167             : /// 0x100: unsigned
     168        1215 :   __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(void,1)
     169        1577 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(char,(CHAR_MIN==0)*0x100+3)
     170             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(signed char,3)
     171             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned char,0x100+3)
     172             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(short,3)
     173             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned short,0x100+3)
     174      259694 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(int,3)
     175          98 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned int,0x100+3)
     176      237805 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long,3)
     177             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long,0x100+3)
     178             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long long,3)
     179             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long long,0x100+3)
     180           0 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(float,4)
     181      275466 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(double,4)
     182             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long double,4)
     183         758 :   __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(FILE,5)
     184             : 
     185             :   ~TypesafePtr() {
     186     1786693 :   }
     187             : 
     188             : 
     189             :   TypesafePtr(const TypesafePtr&other) = delete;
     190             : 
     191             :   TypesafePtr & operator=(const TypesafePtr & other) = delete;
     192             : 
     193          64 :   TypesafePtr(TypesafePtr&&other):
     194          64 :     buffer(other.buffer),
     195          64 :     ptr(other.ptr==&other.buffer[0] ? &buffer[0] : other.ptr),
     196          64 :     nelem(other.nelem),
     197          64 :     shape(other.shape),
     198          64 :     flags(other.flags)
     199             :   {
     200          64 :     other.ptr=nullptr;
     201          64 :   }
     202             : 
     203             :   TypesafePtr copy() const;
     204             : 
     205      791537 :   TypesafePtr & operator=(TypesafePtr && other) {
     206      791537 :     ptr=(other.ptr==&other.buffer[0] ? &buffer[0] : other.ptr);
     207      791537 :     flags=other.flags;
     208      791537 :     nelem=other.nelem;
     209      791537 :     shape=other.shape;
     210      791537 :     buffer=other.buffer;
     211      791537 :     other.ptr=nullptr;
     212      791537 :     return *this;
     213             :   }
     214             : 
     215           5 :   std::string type_str() const {
     216           5 :     auto type=(flags>>16)&0xff;
     217           5 :     if(type==0) return "wildcard";
     218           1 :     if(type==1) return "void";
     219           0 :     if(type==2) return "integral";
     220           0 :     if(type==3) return "integral";
     221           4 :     if(type==4) return "floating point";
     222           0 :     if(type==5) return "FILE";
     223           0 :     return "unknown";
     224             :   }
     225             : 
     226             : private:
     227             : 
     228             :   template<typename T>
     229     1004481 :   T* get_priv(std::size_t nelem, const std::size_t* shape, bool byvalue) const {
     230             : 
     231     1004481 :     if(typesafePtrSkipCheck()) return (T*) ptr;
     232             :     typedef typename std::remove_pointer<T>::type T_noptr;
     233     1004481 :     if(flags==0) return (T*) ptr; // no check
     234      998130 :     auto size=flags&0xffff;
     235      998130 :     auto type=(flags>>16)&0xff;
     236             :     // auto unsi=(flags>>24)&0x1; // ignored
     237      999841 :     auto cons=(flags>>25)&0x7;
     238             : 
     239             :     // type=0: ignore check
     240             :     // type>5: undefined yet
     241      998130 :     if(type!=0 && type<=5) {
     242      510828 :       if(std::is_integral<T_noptr>::value && (type!=is_integral)) {
     243          20 :         throw ExceptionTypeError() <<"This command expects an integer type. Received a " << type_str() << " instead"<<extra_msg();
     244             :       }
     245      486542 :       if(std::is_floating_point<T_noptr>::value && (type!=is_floating_point)) {
     246           0 :         throw ExceptionTypeError() <<"This command expects a floating point type. Received a " << type_str() << " instead"<<extra_msg();
     247             :       }
     248         759 :       if(std::is_same<FILE,typename std::remove_const<T_noptr>::type>::value && (type!=is_file)) {
     249           0 :         throw ExceptionTypeError() <<"This command expects a FILE. Received a " << type_str() << " instead"<<extra_msg();
     250             :       }
     251             :     }
     252             : 
     253      998125 :     if(size>0 && typesafePtrSizeof<T_noptr>() >0 && typesafePtrSizeof<T_noptr>()!=size) {
     254           0 :       throw ExceptionTypeError() << "This command expects a type with size " << typesafePtrSizeof<T_noptr>() << ". Received type has size " << size << " instead"<<extra_msg();
     255             :     }
     256             : 
     257      999836 :     if(!byvalue) if(cons==1) {
     258           3 :         throw ExceptionTypeError() << "This command is trying to take the address of an argument that was passed by value"<<extra_msg();
     259             :       }
     260             : 
     261             :     // cons==1 (by value) is here treated as cons==3 (const type*)
     262      996394 :     if(cons>0) {
     263             :       if(!std::is_pointer<T>::value) {
     264             :         if(std::is_void<T>::value) {
     265        1711 :           if(cons==1) {
     266           0 :             throw ExceptionTypeError() << "This command expects a void pointer. It received a value instead"<<extra_msg();
     267             :           }
     268             :         } else {
     269      994567 :           if(cons!=1 && cons!=2 && cons!=3) {
     270           0 :             throw ExceptionTypeError() << "This command expects a pointer or a value. It received a pointer-to-pointer instead"<<extra_msg();
     271             :           }
     272             :         }
     273             :         if(!std::is_const<T>::value) {
     274      415882 :           if(cons==3) {
     275           0 :             throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a non modifiable pointer instead (const T*)"<<extra_msg();
     276      415882 :           } else if(cons==1) {
     277           0 :             throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a value instead (T)"<<extra_msg();
     278             :           }
     279             :         }
     280             :       } else {
     281             :         if(!std::is_const<T>::value) {
     282         116 :           if(cons==1) throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a value intead"<<extra_msg();
     283         116 :           if(cons==2 || cons==3) throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a pointer intead"<<extra_msg();
     284             :           if(!std::is_const<T_noptr>::value) {
     285             :             if(cons!=4) throw ExceptionTypeError() << "This command expects a modifiable pointer-to-pointer (T**)"<<extra_msg();
     286             :           } else {
     287         116 :             if(cons!=6) throw ExceptionTypeError() << "This command expects a modifiable pointer to unmodifiable pointer (const T**)"<<extra_msg();
     288             :           }
     289             :         } else {
     290             :           if(!std::is_const<T_noptr>::value) {
     291             :             if(cons!=4 && cons!=5) throw ExceptionTypeError() << "This command expects T*const* pointer, and can only receive T**  or T*const* pointers"<<extra_msg();
     292             :           }
     293             :         }
     294             :       }
     295             :     }
     296             :     // check full shape, if possible
     297      999835 :     if(shape && shape[0] && this->shape[0]) {
     298         675 :       for(unsigned i=0; i<this->shape.size(); i++) {
     299         675 :         if(shape[i]==0 && this->shape[i]!=0) {
     300           0 :           throw ExceptionTypeError() << "Incorrect number of axis (passed greater than requested)"<<extra_msg();
     301             :         }
     302         675 :         if(shape[i]!=0 && this->shape[i]==0) {
     303           0 :           throw ExceptionTypeError() << "Incorrect number of axis (requested greater than passed)"<<extra_msg();
     304             :         }
     305         675 :         if(shape[i]==0) break;
     306         450 :         if((shape[i]>this->shape[i])) {
     307           0 :           throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << this->shape[i] << " have been passed"<<extra_msg();
     308             :         }
     309         450 :         if(i>0 && (shape[i]<this->shape[i])) {
     310           0 :           throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << this->shape[i] << " have been passed"<<extra_msg();
     311             :         }
     312             :       }
     313             :     }
     314      999835 :     if(nelem==0 && shape && shape[0]>0) {
     315      284900 :       nelem=1;
     316      854700 :       for(unsigned i=0; i<this->shape.size(); i++) {
     317      854700 :         if(shape[i]==0) break;
     318      569800 :         nelem*=shape[i];
     319             :       }
     320             :     }
     321             :     // check number of elements
     322      999835 :     if(nelem>0 && this->nelem>0) if(!(nelem<=this->nelem)) {
     323           8 :         throw ExceptionTypeError() << "This command wants to access " << nelem << " from this pointer, but only " << this->nelem << " have been passed"<<extra_msg();
     324             :       }
     325      999833 :     return (T*) ptr;
     326             :   }
     327             : 
     328             : public:
     329             : 
     330             :   template<typename T>
     331        6814 :   void set(T val) const {
     332        6814 :     *get_priv<T>(0,nullptr,false)=val;
     333        6814 :   }
     334             : 
     335             :   template<typename T>
     336      425662 :   typename std::enable_if<std::is_pointer<T>::value,T>::type get() const {
     337             :     typedef typename std::remove_pointer<T>::type T_noptr;
     338      430248 :     return get_priv<T_noptr>(0,nullptr,false);
     339             :   }
     340             : 
     341             :   template<typename T>
     342      267996 :   typename std::enable_if<!std::is_pointer<T>::value,T>::type get() const {
     343      267996 :     return *get_priv<const T>(1,nullptr,true);
     344             :   }
     345             : 
     346             :   template<typename T>
     347             :   T get(std::size_t nelem) const {
     348             :     static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
     349             :     typedef typename std::remove_pointer<T>::type T_noptr;
     350        5999 :     return get_priv<T_noptr>(nelem,nullptr,false);
     351             :   }
     352             : 
     353             :   template<typename T>
     354      293420 :   T get(std::initializer_list<std::size_t> shape) const {
     355             :     static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
     356      293420 :     plumed_assert(shape.size()<=maxrank);
     357             :     std::array<std::size_t,maxrank+1> shape_;
     358             :     typedef typename std::remove_pointer<T>::type T_noptr;
     359             :     unsigned j=0;
     360      880260 :     for(auto i : shape) {
     361      586840 :       shape_[j]=i;
     362      586840 :       j++;
     363             :     }
     364      293420 :     shape_[j]=0;
     365      293420 :     return get_priv<T_noptr>(0,&shape_[0],false);
     366             :   }
     367             : 
     368             :   operator bool() const noexcept {
     369      878943 :     return ptr;
     370             :   }
     371             : 
     372             :   void* getRaw() const noexcept {
     373         222 :     return ptr;
     374             :   }
     375             : 
     376             :   std::size_t getNelem() const noexcept {
     377         222 :     return nelem;
     378             :   }
     379             : 
     380             :   const std::size_t* getShape() const noexcept {
     381             :     return shape.data();
     382             :   }
     383             : 
     384             :   std::size_t getFlags() const noexcept {
     385         222 :     return flags;
     386             :   }
     387             : 
     388             : private:
     389             :   std::array<char,32> buffer;
     390             :   void* ptr=nullptr;
     391             :   std::size_t nelem=0;
     392             :   std::array<std::size_t,maxrank+1> shape; // make sure to initialize this!
     393             :   std::size_t flags=0;
     394             : };
     395             : 
     396             : }
     397             : 
     398             : 
     399             : #endif

Generated by: LCOV version 1.15