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 3730749 : static inline bool typesafePtrSkipCheck() {
44 3730749 : static const bool ret=std::getenv("PLUMED_TYPESAFE_IGNORE");
45 3730749 : return ret;
46 : }
47 :
48 : template<class T>
49 3716489 : std::size_t typesafePtrSizeof() {
50 3716489 : 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 dim): size(dim) {}
75 912134 : SizeLike(unsigned dim): size(dim) {}
76 1658125 : SizeLike(long unsigned dim): size(dim) {}
77 : SizeLike(long long unsigned dim): size(dim) {}
78 : SizeLike(short dim): size(std::size_t(dim)) {}
79 7199 : SizeLike(int dim): size(std::size_t(dim)) {}
80 : SizeLike(long int dim): size(std::size_t(dim)) {}
81 : SizeLike(long long int dim): size(std::size_t(dim)) {}
82 : };
83 :
84 2384748 : inline void init_shape(const std::size_t* shape) {
85 2384748 : myshape[0]=0;
86 2384748 : if(shape && *shape>0) {
87 : std::size_t nelem_=1;
88 : unsigned i=0;
89 1441612 : for(i=0; i<myshape.size(); i++) {
90 1441612 : myshape[i]=*shape;
91 1441612 : if(*shape==0) {
92 : break;
93 : }
94 898574 : nelem_*=*shape;
95 898574 : shape++;
96 : }
97 543038 : plumed_assert(i<myshape.size()); // check that last element is actually zero
98 543038 : if(mynelem==0) {
99 542976 : mynelem=nelem_;
100 : }
101 543038 : plumed_assert(mynelem==nelem_) << "Inconsistent shape/nelem";
102 : }
103 2384748 : }
104 :
105 : static std::string extra_msg();
106 :
107 : public:
108 :
109 : TypesafePtr(void* ptr, std::size_t nelem, const std::size_t* shape, std::size_t flags):
110 1121371 : myptr(ptr),
111 1121371 : mynelem(nelem),
112 1121371 : myflags(flags) {
113 1121371 : buffer[0]='\0';
114 1121371 : init_shape(shape);
115 : }
116 :
117 : static constexpr unsigned maxrank=4;
118 : static TypesafePtr fromSafePtr(void* safe);
119 : static TypesafePtr setNelemAndShape(const TypesafePtr &other, std::size_t nelem, const std::size_t* shape) {
120 533411 : return TypesafePtr(other.myptr,nelem,shape,other.myflags);
121 : }
122 : static TypesafePtr unchecked(const void* ptr) {
123 : return TypesafePtr(const_cast<void*>(ptr),0,nullptr,0);
124 : }
125 : static constexpr unsigned short is_integral=3;
126 : static constexpr unsigned short is_floating_point=4;
127 : static constexpr unsigned short is_file=5;
128 :
129 2723347 : TypesafePtr() {
130 2723347 : myshape[0]=0;
131 2723347 : buffer[0]='\0';
132 : }
133 :
134 339269 : TypesafePtr(std::nullptr_t) {
135 339269 : myshape[0]=0;
136 327971 : buffer[0]='\0';
137 : }
138 :
139 : /// Macro that generate a constructor with given type and flags
140 : #define __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type,type_,flags_) \
141 : TypesafePtr(type_*ptr, std::size_t nelem=0, const std::size_t* shape=nullptr) : \
142 : myptr((void*)const_cast<type*>(ptr)), \
143 : mynelem(nelem), \
144 : myflags(flags_) \
145 : { \
146 : init_shape(shape); \
147 : buffer[0]='\0'; \
148 : }
149 :
150 : /// Macro that uses __PLUMED_WRAPPER_TYPESAFEPTR_INNER to generate constructors with
151 : /// all possible pointer-const combinations
152 : #define __PLUMED_WRAPPER_TYPESAFEPTR(type, code,size) \
153 : __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type, size | (0x10000*(code)) | (0x2000000*2)) \
154 : __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type, type const, size | (0x10000*(code)) | (0x2000000*3)) \
155 : __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*, size | (0x10000*(code)) | (0x2000000*4)) \
156 : __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type*const, size | (0x10000*(code)) | (0x2000000*5)) \
157 : __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*, size | (0x10000*(code)) | (0x2000000*6)) \
158 : __PLUMED_WRAPPER_TYPESAFEPTR_INNER(type*,type const*const, size | (0x10000*(code)) | (0x2000000*7))
159 :
160 : /// Macro that generates the constructors from empy types (those of which sizeof cannot be computed)
161 : #define __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(type,code) __PLUMED_WRAPPER_TYPESAFEPTR(type,code,0)
162 :
163 : /// Macro that generates the constructors from sized types (those of which sizeof can be computed).
164 : /// In addition to generating constructors with all pointer types, it generates a constructor to
165 : /// allow pass-by-value
166 : #define __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(type,code) \
167 : __PLUMED_WRAPPER_TYPESAFEPTR(type,code,sizeof(type)) \
168 : TypesafePtr(type val, std::size_t nelem=0, const std::size_t* shape=nullptr): \
169 : mynelem(1), \
170 : myflags(sizeof(type) | (0x10000*(code)) | (0x2000000*1)) \
171 : { \
172 : plumed_assert(sizeof(type)<=32); \
173 : myptr=&buffer[0]; \
174 : myflags=sizeof(type) | (0x10000*(code)) | (0x2000000*1); \
175 : std::memcpy(&buffer[0],&val,sizeof(type)); \
176 : init_shape(shape); \
177 : }
178 :
179 : /// Here we create all the required instances
180 : /// 1: void
181 : /// 3: integral
182 : /// 4: floating
183 : /// 5: FILE
184 : /// 0x100: unsigned
185 2933 : __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(void,1)
186 2957 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(char,(CHAR_MIN==0)*0x100+3)
187 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(signed char,3)
188 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned char,0x100+3)
189 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(short,3)
190 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned short,0x100+3)
191 323379 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(int,3)
192 118 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned int,0x100+3)
193 112 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long,3)
194 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long,0x100+3)
195 289129 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long long,3)
196 3939 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(unsigned long long,0x100+3)
197 24 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(float,4)
198 452934 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(double,4)
199 : __PLUMED_WRAPPER_TYPESAFEPTR_SIZED(long double,4)
200 1387 : __PLUMED_WRAPPER_TYPESAFEPTR_EMPTY(FILE,5)
201 :
202 : ~TypesafePtr() {
203 4855055 : }
204 :
205 :
206 : TypesafePtr(const TypesafePtr&other) = delete;
207 :
208 : TypesafePtr & operator=(const TypesafePtr & other) = delete;
209 :
210 : TypesafePtr(TypesafePtr&&other):
211 : buffer(other.buffer),
212 : myptr(other.myptr==&other.buffer[0] ? &buffer[0] : other.myptr),
213 : mynelem(other.mynelem),
214 : myshape(other.myshape),
215 : myflags(other.myflags) {
216 : other.myptr=nullptr;
217 : }
218 :
219 : TypesafePtr copy() const;
220 :
221 1913723 : TypesafePtr & operator=(TypesafePtr && other) {
222 1913723 : myptr=(other.myptr==&other.buffer[0] ? &buffer[0] : other.myptr);
223 1913723 : myflags=other.myflags;
224 1913723 : mynelem=other.mynelem;
225 1913723 : myshape=other.myshape;
226 1913723 : buffer=other.buffer;
227 1913723 : other.myptr=nullptr;
228 1913723 : return *this;
229 : }
230 :
231 7 : std::string type_str() const {
232 7 : auto type=(myflags>>16)&0xff;
233 : if(type==0) {
234 0 : return "wildcard";
235 : }
236 : if(type==1) {
237 2 : return "void";
238 : }
239 : if(type==2) {
240 0 : return "integral";
241 : }
242 : if(type==3) {
243 0 : return "integral";
244 : }
245 : if(type==4) {
246 5 : return "floating point";
247 : }
248 : if(type==5) {
249 0 : return "FILE";
250 : }
251 0 : return "unknown";
252 : }
253 :
254 : private:
255 :
256 : template<typename T>
257 3723722 : T* get_priv(std::size_t nelem, const std::size_t* shape, bool byvalue) const {
258 :
259 3723722 : if(typesafePtrSkipCheck()) {
260 0 : return (T*) myptr;
261 : }
262 : typedef typename std::remove_pointer<T>::type T_noptr;
263 3723722 : if(myflags==0) {
264 592 : return (T*) myptr; // no check
265 : }
266 3717901 : auto size=myflags&0xffff;
267 3717901 : auto type=(myflags>>16)&0xff;
268 : // auto unsi=(flags>>24)&0x1; // ignored
269 3715931 : auto cons=(myflags>>25)&0x7;
270 :
271 : // type=0: ignore check
272 : // type>5: undefined yet
273 3717901 : if(type!=0 && type<=5) {
274 716126 : if(std::is_integral<T_noptr>::value && (type!=is_integral)) {
275 24 : throw ExceptionTypeError() <<"This command expects an integer type. Received a " << type_str() << " instead"<<extra_msg();
276 : }
277 3000370 : if(std::is_floating_point<T_noptr>::value && (type!=is_floating_point)) {
278 4 : throw ExceptionTypeError() <<"This command expects a floating point type. Received a " << type_str() << " instead"<<extra_msg();
279 : }
280 1404 : if(std::is_same<FILE,typename std::remove_const<T_noptr>::type>::value && (type!=is_file)) {
281 0 : throw ExceptionTypeError() <<"This command expects a FILE. Received a " << type_str() << " instead"<<extra_msg();
282 : }
283 : }
284 :
285 3717894 : if(size>0 && typesafePtrSizeof<T_noptr>() >0 && typesafePtrSizeof<T_noptr>()!=size) {
286 24 : throw ExceptionTypeError() << "This command expects a type with size " << typesafePtrSizeof<T_noptr>() << ". Received type has size " << size << " instead"<<extra_msg();
287 : }
288 :
289 3723117 : if(!byvalue)
290 3306092 : if(cons==1) {
291 3 : throw ExceptionTypeError() << "This command is trying to take the address of an argument that was passed by value"<<extra_msg();
292 : }
293 :
294 : // cons==1 (by value) is here treated as cons==3 (const type*)
295 3715917 : if(cons>0) {
296 : if(!std::is_pointer<T>::value) {
297 : if(std::is_void<T>::value) {
298 5229 : if(cons==1) {
299 0 : throw ExceptionTypeError() << "This command expects a void pointer. It received a value instead"<<extra_msg();
300 : }
301 : } else {
302 3710548 : if(cons!=1 && cons!=2 && cons!=3) {
303 0 : throw ExceptionTypeError() << "This command expects a pointer or a value. It received a pointer-to-pointer instead"<<extra_msg();
304 : }
305 : }
306 : if(!std::is_const<T>::value) {
307 1606266 : if(cons==3) {
308 6 : throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a non modifiable pointer instead (const T*)"<<extra_msg();
309 1606264 : } else if(cons==1) {
310 0 : throw ExceptionTypeError() << "This command expects a modifiable pointer (T*). It received a value instead (T)"<<extra_msg();
311 : }
312 : }
313 : } else {
314 : if(!std::is_const<T>::value) {
315 140 : if(cons==1) {
316 0 : throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a value instead"<<extra_msg();
317 : }
318 140 : if(cons==2 || cons==3) {
319 0 : throw ExceptionTypeError() << "This command expects a pointer-to-pointer. It received a pointer instead"<<extra_msg();
320 : }
321 : if(!std::is_const<T_noptr>::value) {
322 : if(cons!=4) {
323 : throw ExceptionTypeError() << "This command expects a modifiable pointer-to-pointer (T**)"<<extra_msg();
324 : }
325 : } else {
326 140 : if(cons!=6) {
327 0 : throw ExceptionTypeError() << "This command expects a modifiable pointer to unmodifiable pointer (const T**)"<<extra_msg();
328 : }
329 : }
330 : } else {
331 : if(!std::is_const<T_noptr>::value) {
332 : if(cons!=4 && cons!=5) {
333 : throw ExceptionTypeError() << "This command expects T*const* pointer, and can only receive T** or T*const* pointers"<<extra_msg();
334 : }
335 : }
336 : }
337 : }
338 : }
339 : // check full shape, if possible
340 3723114 : if(shape && shape[0] && myshape[0]) {
341 2378476 : for(unsigned i=0; i<myshape.size(); i++) {
342 2378476 : if(shape[i]==0 && myshape[i]!=0) {
343 12 : throw ExceptionTypeError() << "Incorrect number of axis (passed greater than requested)"<<extra_msg();
344 : }
345 2378472 : if(shape[i]!=0 && myshape[i]==0) {
346 12 : throw ExceptionTypeError() << "Incorrect number of axis (requested greater than passed)"<<extra_msg();
347 : }
348 2378468 : if(shape[i]==0) {
349 : break;
350 : }
351 1582069 : if((shape[i]>myshape[i])) {
352 69 : throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << myshape[i] << " have been passed"<<extra_msg();
353 : }
354 1582046 : if(i>0 && (shape[i]<myshape[i])) {
355 6 : throw ExceptionTypeError() << "This command wants " << shape[i] << " elements on axis " << i <<" of this pointer, but " << myshape[i] << " have been passed"<<extra_msg();
356 : }
357 : }
358 : }
359 3723081 : if(nelem==0 && shape && shape[0]>0) {
360 1664905 : nelem=1;
361 4850938 : for(unsigned i=0; i<myshape.size(); i++) {
362 4850938 : if(shape[i]==0) {
363 : break;
364 : }
365 3186033 : nelem*=shape[i];
366 : }
367 : }
368 : // check number of elements
369 3723081 : if(nelem>0 && mynelem>0)
370 1100635 : if(!(nelem<=mynelem)) {
371 8 : throw ExceptionTypeError() << "This command wants to access " << nelem << " from this pointer, but only " << mynelem << " have been passed"<<extra_msg();
372 : }
373 3723079 : return (T*) myptr;
374 : }
375 :
376 : public:
377 :
378 : template<typename T>
379 86086 : void set(T val) const {
380 86086 : *get_priv<T>(0,nullptr,false)=val;
381 86086 : }
382 :
383 : template<typename T>
384 1540635 : typename std::enable_if<std::is_pointer<T>::value,T>::type get() const {
385 : typedef typename std::remove_pointer<T>::type T_noptr;
386 1548151 : return get_priv<T_noptr>(0,nullptr,false);
387 : }
388 :
389 : template<typename T>
390 417108 : typename std::enable_if<!std::is_pointer<T>::value,T>::type get() const {
391 417108 : return *get_priv<const T>(1,nullptr,true);
392 : }
393 :
394 : // this will make sure that a null character is present
395 7027 : const char* getCString() const {
396 7027 : const char* ptr=get_priv<const char>(0,nullptr,false);
397 7027 : if(!typesafePtrSkipCheck() && mynelem>0 && mynelem<std::numeric_limits<std::size_t>::max()) {
398 : std::size_t i=0;
399 39956 : for(; i<mynelem; i++)
400 39954 : if(ptr[i]==0) {
401 : break;
402 : }
403 1210 : if(i==mynelem) {
404 4 : throw ExceptionTypeError() << "PLUMED is expecting a null terminated string, but no null character was found";
405 : }
406 : }
407 7025 : return ptr;
408 : }
409 :
410 : template<typename T,typename I>
411 : typename std::enable_if<std::is_integral<I>::value, T>::type get(I nelem) const {
412 : static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
413 : typedef typename std::remove_pointer<T>::type T_noptr;
414 : return get_priv<T_noptr>(std::size_t(nelem),nullptr,false);
415 : }
416 :
417 : template<typename T>
418 1665350 : T get(std::initializer_list<SizeLike> shape) const {
419 : static_assert(std::is_pointer<T>::value,"only pointer types allowed here");
420 1665350 : plumed_assert(shape.size()<=maxrank);
421 : std::array<std::size_t,maxrank+1> shape_;
422 : typedef typename std::remove_pointer<T>::type T_noptr;
423 : unsigned j=0;
424 4852216 : for(auto i : shape) {
425 3186866 : shape_[j]=i.size;
426 3186866 : j++;
427 : }
428 1665350 : shape_[j]=0;
429 1665350 : return get_priv<T_noptr>(0,&shape_[0],false);
430 : }
431 :
432 : operator bool() const noexcept {
433 1678974 : return myptr;
434 : }
435 :
436 : void* getRaw() const noexcept {
437 222 : return myptr;
438 : }
439 :
440 : std::size_t getNelem() const noexcept {
441 222 : return mynelem;
442 : }
443 :
444 : const std::size_t* getShape() const noexcept {
445 : return myshape.data();
446 : }
447 :
448 : std::size_t getFlags() const noexcept {
449 222 : return myflags;
450 : }
451 :
452 : private:
453 : std::array<char,32> buffer;
454 : void* myptr=nullptr;
455 : std::size_t mynelem=0;
456 : std::array<std::size_t,maxrank+1> myshape; // make sure to initialize this!
457 : std::size_t myflags=0;
458 : };
459 :
460 : }
461 :
462 :
463 : #endif
|