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
|