Skip to content

Commit db6342d

Browse files
committed
added an alternative method to test if a class has a member function
2 parents 7667c79 + 0dfa8f2 commit db6342d

File tree

5 files changed

+184
-152
lines changed

5 files changed

+184
-152
lines changed

Makefile

Lines changed: 0 additions & 30 deletions
This file was deleted.

PyGlue.H

Lines changed: 124 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/*******************************************************************************
2-
* SOMAR - Stratified Ocean Model with Adaptive Refinement
3-
* Developed by Ed Santilli & Alberto Scotti
2+
* PyGlue
3+
* Developed by Alberto Scotti
44
* Copyright (C) 2018
5-
* Jefferson (Philadelphia University + Thomas Jefferson University) and
65
* University of North Carolina at Chapel Hill
76
*
87
* This library is free software; you can redistribute it and/or
@@ -21,7 +20,7 @@
2120
* USA
2221
*
2322
* For up-to-date contact information, please visit the repository homepage,
24-
* https://github.com/somarhub.
23+
* https://github.com/ululi1970/PyGlue.
2524
******************************************************************************/
2625
#ifndef ___PyGlue_H__INCLUDED___
2726
#define ___PyGlue_H__INCLUDED___
@@ -39,32 +38,41 @@
3938
//#include "AMRNSLevel.H"
4039
#ifdef CHOMBO
4140
#include "FArrayBox.H"
42-
#endif
43-
#define TYPEtoSTR(X) \
44-
{ \
45-
if (std::is_same<T, X>::value) \
46-
{ \
47-
return #X; \
48-
} \
41+
#endif
42+
// these macros are to be used on arithmetic and string types
43+
// note that the chicanery with the pointer is ok
44+
// since at the end of the day the whole expression
45+
// is evaluated only if X and T are the same type.
46+
#define IFTYPEU(X, Y) \
47+
{ \
48+
if (std::is_same<T, X>::value) \
49+
{ \
50+
X s = this->Y(a_pin); \
51+
void *p = static_cast<void *>(&s); \
52+
T *pr = reinterpret_cast<T *>(p); \
53+
t = *pr; \
54+
return; \
55+
} \
4956
}
5057

51-
// this macro is to be used on arithmetic types
52-
#define IFTYPEU(X, Y) \
53-
{ \
54-
if (std::is_same<T, X>::value) \
55-
{ \
56-
t = static_cast<X>(this->Y(a_pin)); \
57-
return; \
58-
} \
58+
#define IFTYPEP(X, Y) \
59+
{ \
60+
if (std::is_same<T, X>::value) \
61+
{ \
62+
\
63+
void *pr = static_cast<void *>(const_cast<T *>(&t)); \
64+
X *p = reinterpret_cast<X *>(pr); \
65+
return this->Y(*p); \
66+
} \
5967
}
60-
#define IFTYPEP(X, Y) \
68+
#define TYPEtoSTR(X) \
6169
{ \
6270
if (std::is_same<T, X>::value) \
6371
{ \
64-
T x = t; \
65-
return this->Y(static_cast<X>(x)); \
72+
return #X; \
6673
} \
6774
}
75+
6876
#define SAME(X) std::is_same<T, X>::value
6977
// The purpose of this class is to be able to run functions on Python
7078
// from within a C++ code. The use is
@@ -91,23 +99,77 @@
9199

92100
#include <array>
93101

94-
template<typename> struct PM_traits{
102+
//dark magic used to check if a class as a .size() function.
103+
template <typename... Ts>
104+
struct make_void
105+
{
106+
typedef void type;
107+
};
108+
template <typename... Ts>
109+
using void_t = typename make_void<Ts...>::type;
110+
// note the above is not needed if using c++14,
111+
template <typename T, typename = void>
112+
struct has_size_func : std::false_type
113+
{
114+
}; // default to false if third argument cannot work
115+
116+
template <typename T>
117+
struct has_size_func<T, void_t<decltype(std::declval<T &>().size())>> : std::true_type
118+
{
119+
};
120+
// If the instantation of the Expression works, we set it to true, else we default to the false type
121+
122+
/* the following is an alternative version that achieve the same result
123+
124+
// simple routine to test if a class has a member named size of numbytes (or whatever)
125+
// The caveat is that the member has to have a return type for which sizeof()
126+
// must be defined (so this rules out void functions).
127+
//
128+
// example
129+
// ...
130+
// class A{ int size();};
131+
// has_size<A>::value; // this will return TRUE
132+
// has_size<int>::value; // this will return FALSE
133+
//
134+
// It works because when the static member ::value is calculated during
135+
// instantiation, it will try first to use one of the test functions
136+
// with an actual template. Only if those fails because the return value cannot
137+
// be deduced, it will fall back on the default one, which has size M;
138+
// it is possible to specify M if, for some weird reason, you expect
139+
// siaeof(T::size()) to have size equal to 1000;
140+
*/
141+
template <typename T, int M = 1000>
142+
struct has_size
143+
{
144+
private:
145+
typedef char no[M];
146+
template <typename C>
147+
static auto test(C a) -> decltype(std::declval<C>().size());
148+
template <typename>
149+
static no &test(...);
150+
151+
T t;
152+
153+
public:
154+
static const bool value = sizeof(test<T>(t)) != sizeof(no);
95155
};
96-
template<class T, class U> struct PM_traits<U T::*> {
97-
using member_type = U;
98-
}; //dark magic used to check if a class as a .size() function.
156+
157+
158+
159+
// So now we have a mechanism to detect Expression
160+
161+
// now we work out the specific expression we need
99162

100163
#include "MayDay.H"
101164

102165
class Py
103166
{
104167
private:
105-
106168
//forward declarations
107169
#ifdef CHOMBO
108-
PyObject *packFAB(FArrayBox &a_q);
170+
PyObject *packFAB(FArrayBox &a_q);
109171
PyObject *packFAB(const FArrayBox &a_q);
110-
#endif
172+
#endif
111173
PyObject *packDouble(double a_x);
112174
PyObject *packFloat(float a_x);
113175
PyObject *packInt(int a_i);
@@ -121,7 +183,7 @@ PyObject *packFAB(FArrayBox &a_q);
121183

122184
void runVoidFunction(std::string Module, std::string function, std::vector<PyObject *> arg);
123185
PyObject *runFunction(std::string Module, std::string function, std::vector<PyObject *> arg);
124-
186+
125187
enum
126188
{
127189
MULTIPLE_INSTANCES_DETECTED = 0,
@@ -165,27 +227,25 @@ PyObject *packFAB(FArrayBox &a_q);
165227
template <class U, class T>
166228
PyObject *makeView(U &a_v)
167229
{
168-
230+
169231
TypeOf<T>(); // will stop compilation if T is not in the list
170-
232+
171233
unsigned long long size = a_v.size() * sizeof(T);
172234
char *p = reinterpret_cast<char *>(&(a_v[0]));
173235
return PyMemoryView_FromMemory(p, size, PyBUF_WRITE);
174236
}
175-
template <class U, class T>
237+
template <class U, class T>
176238
PyObject *makeView(const U &a_v)
177239
{
178-
240+
179241
TypeOf<T>(); // will stop compilation if T is not in the list
180-
242+
181243
unsigned long long size = a_v.size() * sizeof(T);
182-
char *p = reinterpret_cast<char *>(const_cast<T*>(&(a_v[0])));
183-
184-
return PyMemoryView_FromMemory(p, size, PyBUF_READ);
244+
char *p = reinterpret_cast<char *>(const_cast<T *>(&(a_v[0])));
245+
246+
return PyMemoryView_FromMemory(p, size, PyBUF_READ);
185247
}
186-
187-
188-
248+
189249
// basic packing and unpacking template sfor objects that are are passed by reference
190250
template <class T>
191251
void unpack(T &t, PyObject *a_pin)
@@ -197,6 +257,8 @@ template <class U, class T>
197257
IFTYPEU(double, unpackDouble)
198258

199259
IFTYPEU(float, unpackFloat)
260+
261+
IFTYPEU(std::string, unpackString)
200262
this->lintcatcher(CANNOT_UNPACK_TYPE, " unpack");
201263
}
202264

@@ -206,11 +268,11 @@ template <class U, class T>
206268
const bool check_type_is_defined = SAME(bool) || SAME(int) || SAME(float) || SAME(std::string) || SAME(double);
207269
static_assert(check_type_is_defined, " Only bool, int, float, double and std::string are supported, but feel free to add to the list and post back");
208270
T x = t;
209-
IFTYPEP(bool,packBool)
271+
IFTYPEP(bool, packBool)
210272
IFTYPEP(int, packInt)
211273
IFTYPEP(double, packDouble)
212274
IFTYPEP(float, packFloat)
213-
275+
IFTYPEP(std::string, packString)
214276

215277
this->lintcatcher(CANNOT_PACK_TYPE, " pack"); //TODO: catch exception at compile time.
216278
return static_cast<PyObject *>(nullptr);
@@ -221,53 +283,51 @@ template <class U, class T>
221283
{
222284
const bool check_type_is_defined = SAME(bool) || SAME(int) || SAME(float) || SAME(std::string) || SAME(double);
223285
static_assert(check_type_is_defined, " Only bool, int, float, double and std::string are supported, but feel free to add to the list and post back");
224-
225-
IFTYPEP(bool,packBool)
286+
287+
IFTYPEP(bool, packBool)
226288
IFTYPEP(int, packInt)
227289
IFTYPEP(double, packDouble)
228290
IFTYPEP(float, packFloat)
229-
291+
IFTYPEP(std::string, packString)
230292

231293
this->lintcatcher(CANNOT_PACK_TYPE, " pack"); //TODO: catch exception at compile time.
232294
return static_cast<PyObject *>(nullptr);
233295
};
234296

235-
236297
template <class U, class T>
237-
PyObject* packCont( U & a_v){
238-
PyObject *pView = makeView<U,T>(a_v);
298+
PyObject *packCont(U &a_v)
299+
{
300+
PyObject *pView = makeView<U, T>(a_v);
239301
PyObject *pSize = PyLong_FromLong(a_v.size());
240302
PyObject *pTypeOfT = PyUnicode_FromString(TypeOf<T>().c_str());
241303
PyObject *pArgs = PyTuple_New(3);
242304
PyTuple_SetItem(pArgs, 0, pSize);
243305
PyTuple_SetItem(pArgs, 1, pTypeOfT);
244306
PyTuple_SetItem(pArgs, 2, pView);
245307
return pArgs;
246-
247308
}
248309

249310
template <class U, class T>
250-
PyObject* packCont( const U & a_v){
251-
PyObject *pView = makeView<U,T>(a_v);
311+
PyObject *packCont(const U &a_v)
312+
{
313+
PyObject *pView = makeView<U, T>(a_v);
252314
PyObject *pSize = PyLong_FromLong(a_v.size());
253315
PyObject *pTypeOfT = PyUnicode_FromString(TypeOf<T>().c_str());
254316
PyObject *pArgs = PyTuple_New(3);
255317
PyTuple_SetItem(pArgs, 0, pSize);
256318
PyTuple_SetItem(pArgs, 1, pTypeOfT);
257319
PyTuple_SetItem(pArgs, 2, pView);
258320
return pArgs;
259-
260321
}
261322
template <class T>
262-
inline PyObject *pack(std::valarray<T> &a_v) { return this->packCont<std::valarray<T>,T>(a_v); };
323+
inline PyObject *pack(std::valarray<T> &a_v) { return this->packCont<std::valarray<T>, T>(a_v); };
263324
template <class T>
264-
inline PyObject *pack(const std::valarray<T> &a_v) { return this->packCont<std::valarray<T>,T>(a_v); };
325+
inline PyObject *pack(const std::valarray<T> &a_v) { return this->packCont<std::valarray<T>, T>(a_v); };
265326
template <class T>
266-
inline PyObject *pack(std::vector<T> &a_v) { return this->packCont<std::vector<T>,T>(a_v); };
327+
inline PyObject *pack(std::vector<T> &a_v) { return this->packCont<std::vector<T>, T>(a_v); };
267328
template <class T>
268-
inline PyObject *pack(const std::vector<T> &a_v) { return this->packCont<std::vector<T>,T>(a_v); };
269-
270-
329+
inline PyObject *pack(const std::vector<T> &a_v) { return this->packCont<std::vector<T>, T>(a_v); };
330+
271331
// bottom of recursion
272332
template <class T>
273333
void BuildArgsVector(T &t)
@@ -306,7 +366,7 @@ public:
306366
{
307367
PyObject *return_value = runFunction(Module, function, m_args);
308368
T t;
309-
unpack(t, return_value);
369+
unpack<T>(t, return_value);
310370
Py_DECREF(return_value);
311371
return t;
312372
};
@@ -338,31 +398,14 @@ private:
338398
//specializations
339399
#ifdef CHOMBO
340400
template <>
341-
inline PyObject *Py::pack(FArrayBox &a_q) { return this->packFAB(a_q); }; // return an object that Python uses to alias a_q to a numpy
401+
inline PyObject *Py::pack(FArrayBox &a_q)
402+
{
403+
return this->packFAB(a_q);
404+
}; // return an object that Python uses to alias a_q to a numpy
342405
template <>
343406
inline PyObject *Py::pack(const FArrayBox &a_q) { return this->packFAB(a_q); }; // return an object that Python uses to alias a_q to a numpy
344407
#endif
345408

346-
template <>
347-
inline PyObject *Py::pack(std::string &a_s)
348-
{
349-
std::string s = a_s;
350-
return this->packString(s);
351-
};
352-
353-
template <>
354-
inline PyObject *Py::pack(const std::string &a_s)
355-
{
356-
std::string s = a_s;
357-
return this->packString(s);
358-
};
359-
360-
template <>
361-
inline void Py::unpack(std::string &a_i, PyObject *a_pin)
362-
{
363-
a_i = this->unpackString(a_pin);
364-
};
365-
366409
// In the above specializations, we make local copies to mimic the behavior of a function
367410
// that is called by value. Apparently c++11 does not like mixing and matching of
368411
// templates by reference and by value.

0 commit comments

Comments
 (0)