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