LCOV - code coverage report
Current view: top level - tools - TypesafePtr.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 119 140 85.0 %
Date: 2026-03-30 13:16:06 Functions: 40 55 72.7 %

          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     1147455 : static inline bool typesafePtrSkipCheck() {
      42     1147455 :   static const bool ret=std::getenv("PLUMED_TYPESAFE_IGNORE");
      43     1147455 :   return ret;
      44             : }
      45             : 
      46             : template<class T>
      47     1140156 : std::size_t typesafePtrSizeof() {
      48     1140156 :   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     1169576 :   inline void init_shape(const std::size_t* shape) {
      70     1169576 :     this->shape[0]=0;
      71     1169576 :     if(shape && *shape>0) {
      72             :       std::size_t nelem_=1;
      73             :       unsigned i=0;
      74         848 :       for(i=0; i<this->shape.size(); i++) {
      75         848 :         this->shape[i]=*shape;
      76         848 :         if(*shape==0) {
      77             :           break;
      78             :         }
      79         484 :         nelem_*=*shape;
      80         484 :         shape++;
      81             :       }
      82         364 :       plumed_assert(i<this->shape.size()); // check that last element is actually zero
      83         364 :       if(nelem==0) {
      84         364 :         nelem=nelem_;
      85             :       }
      86         364 :       plumed_assert(nelem==nelem_) << "Inconsistent shape/nelem";
      87             :     }
      88     1169576 :   }
      89             : 
      90             :   static std::string extra_msg();
      91             : 
      92             : public:
      93             : 
      94             :   TypesafePtr(void* ptr, std::size_t nelem, const std::size_t* shape, std::size_t flags):
      95      306661 :     ptr(ptr),
      96      306661 :     nelem(nelem),
      97      306661 :     flags(flags) {
      98      306661 :     buffer[0]='\0';
      99      306661 :     init_shape(shape);
     100             :   }
     101             : 
     102             :   static const unsigned maxrank=4;
     103             :   static TypesafePtr fromSafePtr(void* safe);
     104             :   static TypesafePtr setNelemAndShape(const TypesafePtr &other, std::size_t nelem, const std::size_t* shape) {
     105      290112 :     return TypesafePtr(other.ptr,nelem,shape,other.flags);
     106             :   }
     107             :   static TypesafePtr unchecked(const void* ptr) {
     108             :     return TypesafePtr(const_cast<void*>(ptr),0,nullptr,0);
     109             :   }
     110             :   static constexpr unsigned short is_integral=3;
     111             :   static constexpr unsigned short is_floating_point=4;
     112             :   static constexpr unsigned short is_file=5;
     113             : 
     114     1735769 :   TypesafePtr() {
     115     1735769 :     shape[0]=0;
     116     1503261 :     buffer[0]='\0';
     117             :   }
     118             : 
     119      262966 :   TypesafePtr(std::nullptr_t) {
     120      262966 :     shape[0]=0;
     121      261819 :     buffer[0]='\0';
     122             :   }
     123             : 
     124             : /// Macro that generate a constructor with given type and flags
     125             : #define __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type,type_,flags_) \
     126             :   TypesafePtr(type_*ptr, std::size_t nelem=0, const std::size_t* shape=nullptr) : \
     127             :     ptr((void*)const_cast<type*>(ptr)), \
     128             :     nelem(nelem), \
     129             :     flags(flags_) \
     130             :   { \
     131             :     init_shape(shape); \
     132             :     buffer[0]='\0'; \
     133             :   }
     134             : 
     135             : /// Macro that uses __PLUMED_WRAPPER_TYPESAFEPTR_INNER to generate constructors with
     136             : /// all possible pointer-const combinations
     137             : #define __PLUMED_WRAPPER_TYPESAFEPTR(type, code,size) \
     138             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type,             size | (0x10000*(code)) | (0x2000000*2)) \
     139             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type const,       size | (0x10000*(code)) | (0x2000000*3)) \
     140             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*,            size | (0x10000*(code)) | (0x2000000*4)) \
     141             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*const,       size | (0x10000*(code)) | (0x2000000*5)) \
     142             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*,      size | (0x10000*(code)) | (0x2000000*6)) \
     143             :   __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*const, size | (0x10000*(code)) | (0x2000000*7))
     144             : 
     145             : /// Macro that generates the constructors from empy types (those of which sizeof cannot be computed)
     146             : #define __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(type,code) __PLUMED_WRAPPER_TYPESAFEPTR(type,code,0)
     147             : 
     148             : /// Macro that generates the constructors from sized types (those of which sizeof can be computed).
     149             : /// In addition to generating constructors with all pointer types, it generates a constructor to
     150             : /// allow pass-by-value
     151             : #define __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(type,code) \
     152             :   __PLUMED_WRAPPER_TYPESAFEPTR(type,code,sizeof(type)) \
     153             :   TypesafePtr(type val, std::size_t nelem=0, const std::size_t* shape=nullptr): \
     154             :       nelem(1), \
     155             :       flags(sizeof(type) | (0x10000*(code)) | (0x2000000*1)) \
     156             :     { \
     157             :     plumed_assert(sizeof(type)<=32); \
     158             :     ptr=&buffer[0]; \
     159             :     flags=sizeof(type) | (0x10000*(code)) | (0x2000000*1); \
     160             :     std::memcpy(&buffer[0],&val,sizeof(type)); \
     161             :     init_shape(shape); \
     162             :   }
     163             : 
     164             : /// Here we create all the required instances
     165             : /// 1: void
     166             : /// 3: integral
     167             : /// 4: floating
     168             : /// 5: FILE
     169             : /// 0x100: unsigned
     170        1246 :   __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(void,1)
     171        1815 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(char,(CHAR_MIN==0)*0x100+3)
     172             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(signed char,3)
     173             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned char,0x100+3)
     174             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(short,3)
     175             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned short,0x100+3)
     176      266643 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(int,3)
     177          98 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned int,0x100+3)
     178             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long,3)
     179             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long,0x100+3)
     180      248455 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long long,3)
     181        3939 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long long,0x100+3)
     182           0 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(float,4)
     183      339842 :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(double,4)
     184             :   __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long double,4)
     185         877 :   __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(FILE,5)
     186             : 
     187             :   ~TypesafePtr() {
     188     2320880 :   }
     189             : 
     190             : 
     191             :   TypesafePtr(const TypesafePtr&other) = delete;
     192             : 
     193             :   TypesafePtr & operator=(const TypesafePtr & other) = delete;
     194             : 
     195          64 :   TypesafePtr(TypesafePtr&&other):
     196          64 :     buffer(other.buffer),
     197          64 :     ptr(other.ptr==&other.buffer[0] ? &buffer[0] : other.ptr),
     198          64 :     nelem(other.nelem),
     199          64 :     shape(other.shape),
     200          64 :     flags(other.flags) {
     201          64 :     other.ptr=nullptr;
     202          64 :   }
     203             : 
     204             :   TypesafePtr copy() const;
     205             : 
     206      930040 :   TypesafePtr & operator=(TypesafePtr && other) {
     207      930040 :     ptr=(other.ptr==&other.buffer[0] ? &buffer[0] : other.ptr);
     208      930040 :     flags=other.flags;
     209      930040 :     nelem=other.nelem;
     210      930040 :     shape=other.shape;
     211      930040 :     buffer=other.buffer;
     212      930040 :     other.ptr=nullptr;
     213      930040 :     return *this;
     214             :   }
     215             : 
     216           5 :   std::string type_str() const {
     217           5 :     auto type=(flags>>16)&0xff;
     218           5 :     if(type==0) {
     219           0 :       return "wildcard";
     220             :     }
     221             :     if(type==1) {
     222           1 :       return "void";
     223             :     }
     224             :     if(type==2) {
     225           0 :       return "integral";
     226             :     }
     227             :     if(type==3) {
     228           0 :       return "integral";
     229             :     }
     230             :     if(type==4) {
     231           4 :       return "floating point";
     232             :     }
     233             :     if(type==5) {
     234           0 :       return "FILE";
     235             :     }
     236           0 :     return "unknown";
     237             :   }
     238             : 
     239             : private:
     240             : 
     241             :   template<typename T>
     242     1147455 :   T* get_priv(std::size_t nelem, const std::size_t* shape, bool byvalue) const {
     243             : 
     244     1147455 :     if(typesafePtrSkipCheck()) {
     245           0 :       return (T*) ptr;
     246             :     }
     247             :     typedef typename std::remove_pointer<T>::type T_noptr;
     248     1147455 :     if(flags==0) {
     249        4658 :       return (T*) ptr;  // no check
     250             :     }
     251     1141040 :     auto size=flags&0xffff;
     252     1141040 :     auto type=(flags>>16)&0xff;
     253             :     // auto unsi=(flags>>24)&0x1; // ignored
     254     1142797 :     auto cons=(flags>>25)&0x7;
     255             : 
     256             :     // type=0: ignore check
     257             :     // type>5: undefined yet
     258     1141040 :     if(type!=0 && type<=5) {
     259      536328 :       if(std::is_integral<T_noptr>::value && (type!=is_integral)) {
     260          20 :         throw ExceptionTypeError() <<"This command expects an integer type. Received a " << type_str() << " instead"<<extra_msg();
     261             :       }
     262      603833 :       if(std::is_floating_point<T_noptr>::value && (type!=is_floating_point)) {
     263           0 :         throw ExceptionTypeError() <<"This command expects a floating point type. Received a " << type_str() << " instead"<<extra_msg();
     264             :       }
     265         878 :       if(std::is_same<FILE,typename std::remove_const<T_noptr>::type>::value && (type!=is_file)) {
     266           0 :         throw ExceptionTypeError() <<"This command expects a FILE. Received a " << type_str() << " instead"<<extra_msg();
     267             :       }
     268             :     }
     269             : 
     270     1141035 :     if(size>0 && typesafePtrSizeof<T_noptr>() >0 && typesafePtrSizeof<T_noptr>()!=size) {
     271           0 :       throw ExceptionTypeError() << "This command expects a type with size " << typesafePtrSizeof<T_noptr>() << ". Received type has size " << size << " instead"<<extra_msg();
     272             :     }
     273             : 
     274     1142792 :     if(!byvalue)
     275      862206 :       if(cons==1) {
     276           3 :         throw ExceptionTypeError() << "This command is trying to take the address of an argument that was passed by value"<<extra_msg();
     277             :       }
     278             : 
     279             :     // cons==1 (by value) is here treated as cons==3 (const type*)
     280     1138232 :     if(cons>0) {
     281             :       if(!std::is_pointer<T>::value) {
     282             :         if(std::is_void<T>::value) {
     283        1757 :           if(cons==1) {
     284           0 :             throw ExceptionTypeError() << "This command expects a void pointer. It received a value instead"<<extra_msg();
     285             :           }
     286             :         } else {
     287     1136359 :           if(cons!=1 && cons!=2 && cons!=3) {
     288           0 :             throw ExceptionTypeError() << "This command expects a pointer or a value. It received a pointer-to-pointer instead"<<extra_msg();
     289             :           }
     290             :         }
     291             :         if(!std::is_const<T>::value) {
     292      470194 :           if(cons==3) {
     293           0 :             throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a non modifiable pointer instead (const T*)"<<extra_msg();
     294      470194 :           } else if(cons==1) {
     295           0 :             throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a value instead (T)"<<extra_msg();
     296             :           }
     297             :         }
     298             :       } else {
     299             :         if(!std::is_const<T>::value) {
     300         116 :           if(cons==1) {
     301           0 :             throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a value intead"<<extra_msg();
     302             :           }
     303         116 :           if(cons==2 || cons==3) {
     304           0 :             throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a pointer intead"<<extra_msg();
     305             :           }
     306             :           if(!std::is_const<T_noptr>::value) {
     307             :             if(cons!=4) {
     308             :               throw ExceptionTypeError() << "This command expects a modifiable pointer-to-pointer (T**)"<<extra_msg();
     309             :             }
     310             :           } else {
     311         116 :             if(cons!=6) {
     312           0 :               throw ExceptionTypeError() << "This command expects a modifiable pointer to unmodifiable pointer (const T**)"<<extra_msg();
     313             :             }
     314             :           }
     315             :         } else {
     316             :           if(!std::is_const<T_noptr>::value) {
     317             :             if(cons!=4 && cons!=5) {
     318             :               throw ExceptionTypeError() << "This command expects T*const* pointer, and can only receive T**  or T*const* pointers"<<extra_msg();
     319             :             }
     320             :           }
     321             :         }
     322             :       }
     323             :     }
     324             :     // check full shape, if possible
     325     1142791 :     if(shape && shape[0] && this->shape[0]) {
     326         525 :       for(unsigned i=0; i<this->shape.size(); i++) {
     327         525 :         if(shape[i]==0 && this->shape[i]!=0) {
     328           0 :           throw ExceptionTypeError() << "Incorrect number of axis (passed greater than requested)"<<extra_msg();
     329             :         }
     330         525 :         if(shape[i]!=0 && this->shape[i]==0) {
     331           0 :           throw ExceptionTypeError() << "Incorrect number of axis (requested greater than passed)"<<extra_msg();
     332             :         }
     333         525 :         if(shape[i]==0) {
     334             :           break;
     335             :         }
     336         350 :         if((shape[i]>this->shape[i])) {
     337           0 :           throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << this->shape[i] << " have been passed"<<extra_msg();
     338             :         }
     339         350 :         if(i>0 && (shape[i]<this->shape[i])) {
     340           0 :           throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << this->shape[i] << " have been passed"<<extra_msg();
     341             :         }
     342             :       }
     343             :     }
     344     1142791 :     if(nelem==0 && shape && shape[0]>0) {
     345      309618 :       nelem=1;
     346      928854 :       for(unsigned i=0; i<this->shape.size(); i++) {
     347      928854 :         if(shape[i]==0) {
     348             :           break;
     349             :         }
     350      619236 :         nelem*=shape[i];
     351             :       }
     352             :     }
     353             :     // check number of elements
     354     1142791 :     if(nelem>0 && this->nelem>0)
     355      545597 :       if(!(nelem<=this->nelem)) {
     356           8 :         throw ExceptionTypeError() << "This command wants to access " << nelem << " from this pointer, but only " << this->nelem << " have been passed"<<extra_msg();
     357             :       }
     358     1142789 :     return (T*) ptr;
     359             :   }
     360             : 
     361             : public:
     362             : 
     363             :   template<typename T>
     364        8068 :   void set(T val) const {
     365        8068 :     *get_priv<T>(0,nullptr,false)=val;
     366        8068 :   }
     367             : 
     368             :   template<typename T>
     369      478931 :   typename std::enable_if<std::is_pointer<T>::value,T>::type get() const {
     370             :     typedef typename std::remove_pointer<T>::type T_noptr;
     371      484028 :     return get_priv<T_noptr>(0,nullptr,false);
     372             :   }
     373             : 
     374             :   template<typename T>
     375      280662 :   typename std::enable_if<!std::is_pointer<T>::value,T>::type get() const {
     376      280662 :     return *get_priv<const T>(1,nullptr,true);
     377             :   }
     378             : 
     379             :   template<typename T>
     380             :   T get(std::size_t nelem) const {
     381             :     static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
     382             :     typedef typename std::remove_pointer<T>::type T_noptr;
     383        7361 :     return get_priv<T_noptr>(nelem,nullptr,false);
     384             :   }
     385             : 
     386             :   template<typename T>
     387      367332 :   T get(std::initializer_list<std::size_t> shape) const {
     388             :     static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
     389      367332 :     plumed_assert(shape.size()<=maxrank);
     390             :     std::array<std::size_t,maxrank+1> shape_;
     391             :     typedef typename std::remove_pointer<T>::type T_noptr;
     392             :     unsigned j=0;
     393     1101996 :     for(auto i : shape) {
     394      734664 :       shape_[j]=i;
     395      734664 :       j++;
     396             :     }
     397      367332 :     shape_[j]=0;
     398      367332 :     return get_priv<T_noptr>(0,&shape_[0],false);
     399             :   }
     400             : 
     401             :   operator bool() const noexcept {
     402      989753 :     return ptr;
     403             :   }
     404             : 
     405             :   void* getRaw() const noexcept {
     406         222 :     return ptr;
     407             :   }
     408             : 
     409             :   std::size_t getNelem() const noexcept {
     410         222 :     return nelem;
     411             :   }
     412             : 
     413             :   const std::size_t* getShape() const noexcept {
     414             :     return shape.data();
     415             :   }
     416             : 
     417             :   std::size_t getFlags() const noexcept {
     418         222 :     return flags;
     419             :   }
     420             : 
     421             : private:
     422             :   std::array<char,32> buffer;
     423             :   void* ptr=nullptr;
     424             :   std::size_t nelem=0;
     425             :   std::array<std::size_t,maxrank+1> shape; // make sure to initialize this!
     426             :   std::size_t flags=0;
     427             : };
     428             : 
     429             : }
     430             : 
     431             : 
     432             : #endif

Generated by: LCOV version 1.16