I am fairly familiar with standard metaprogramming solutions (for example, C ++ 11 search methods, if the type has a member function or supports the operator? ) T is a macromagic. However, I have an example of using the following convenience macros (certainly simplified for StackOverflow, of course, but imagine that this is for serialization or something else) ...
#define START(type) do { typedef type current; const char typeName[] = #type #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n", #type, #fieldname, \ std::is_same<decltype(std::declval<current>().fieldname),int> ? "int" : "string") #define END() } while (0) struct Foo { int i; char *j; char *k; }; struct Bar { char *x; int y; }; START(Foo); OUTPUT(i); // type of Foo.i is int OUTPUT(j); // type of Foo.j is string OUTPUT(k); // type of Foo.k is string END(); START(Bar); OUTPUT(x); // type of Bar.x is string OUTPUT(y); // type of Bar.y is int END();
But now let me say that someone comes and adds a new data element to our schema: field pairs (x, xLength) . We want to change our convenience macros like this ...
#define START(obj) do { const auto& current = (obj) #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n", #type, #fieldname, \ std::is_same<decltype(std::declval<current>().fieldname),int> ? "int" : hasfield(current, fieldname##Length) ? "Pascal string" : "C string") #define END() } while (0) struct Baz { char *x, *y, *z; int xLength, zLength; }; START(Baz); OUTPUT(x); // type of Baz.x is Pascal string OUTPUT(y); // type of Baz.y is C string OUTPUT(z); // type of Baz.z is Pascal string END();
I myself was able to come up with the following hasfield implementation that runs on Clang ...
#define hasfield(classtype, fieldname) \ []() { \ struct X { \ template<class T, int=sizeof(&T::fieldname)> static constexpr bool f(T*){ return true; } \ static constexpr bool f(...) { return false; } \ }; return X::f((classtype*)0); \ }()
... but, unfortunately, this is due to a bug in Clang ; according to the C ++ 11 standard, the local class X not allowed to have template members. In fact, this code is not compiled with GCC.
So, I am puzzled: is it possible in C ++ 11 to define the OUTPUT macro so that it does what I want?
Absolute limitations: do not change the definition of the Baz structure. There is no hard coding for fieldname ahead of time.
Nice-to-haves: the hasfield(c,f) macro, which can also be used in other contexts (as opposed to smoothing code directly in the OUTPUT macro). No, assuming that offsetof(c,fLength)==offsetof(c,f)+sizeof(std::declval<c>().f) .