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
|