Is it possible to write macro-socialization (S, f) or hasfield (S, f) in C ++ 11?

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) .

+7
c ++ macros c ++ 11 template-meta-programming
source share
2 answers

You can make this work with some restrictions that may or may not matter to you, inheriting from current and relying on the shadow: declare a local variable fieldname , create a local class that comes from the type you are checking and inside the member function, check, whether fieldname to a local variable. If so, the fieldname member fieldname not exist.

 #include <utility> #include <stdio.h> #define START(type) do { typedef type current; const char typeName[] = #type #define HASMEMBER(fieldname) \ []() -> bool { \ struct HASMEMBER1 { } fieldname; \ struct HASMEMBER2 : current { \ static char TEST1(HASMEMBER1&); \ static char (&TEST1(...))[2]; \ auto TEST2() -> decltype(TEST1(fieldname)); \ }; \ return sizeof(std::declval<HASMEMBER2>().TEST2()) == 2; \ }() #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n", typeName, #fieldname, \ std::is_same<decltype(current::fieldname),int>::value ? "int" : \ HASMEMBER(fieldname##Length) ? "Pascal string" : "C string") #define END() } while (0) struct Foo { int i; char *j; char *k; }; struct Bar { char *x; int y; }; struct Baz { char *x, *y, *z; int xLength, zLength; }; int main() { START(Foo); OUTPUT(i); // type of Foo.i is int OUTPUT(j); // type of Foo.j is C string OUTPUT(k); // type of Foo.k is C string END(); START(Bar); OUTPUT(x); // type of Bar.x is C string OUTPUT(y); // type of Bar.y is int END(); 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(); } 

Edited to work with GCC 4.6.3. It is also still accepted by GCC 4.8.1 and clang 3.3, and should also work with GCC 4.7.3 (but not 4.7.2).

+3
source share

Thanks to @hvd for the clever idea of โ€‹โ€‹finding either a local variable or an inherited member! Here's the exact code that I eventually started working with GCC 4.6.3

 #include <utility> #include <stdio.h> #define START(type) do { typedef type current; const char typeName[] = #type #define HASMEMBER(fieldname) \ []()->bool { \ char fieldname; \ struct HASMEMBER2 : current { \ auto TEST2() -> char[sizeof(fieldname)]; \ }; \ return sizeof(std::declval<HASMEMBER2>().TEST2()) != 1; \ }() #define OUTPUT(fieldname) \ printf("type of %s.%s is %s\n", typeName, #fieldname, \ std::is_same<decltype(current::fieldname),int>::value ? "int" : \ HASMEMBER(fieldname##Length) ? "Pascal string" : "C string") #define END() } while (0) struct Foo { int i; char *j; char *k; }; struct Bar { char *x; int y; }; struct Baz { char *x, *y, *z; int xLength, zLength; }; int main() { START(Foo); OUTPUT(i); // type of Foo.i is int OUTPUT(j); // type of Foo.j is C string OUTPUT(k); // type of Foo.k is C string END(); START(Bar); OUTPUT(x); // type of Bar.x is C string OUTPUT(y); // type of Bar.y is int END(); 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(); } 

Note that this approach cannot distinguish a 1-byte char xLength from the absence of xLength . For my application this is acceptable; all my xLength fields are 4 bytes long or nonexistent.

Note that this HASMEMBER only works for infrequent member variables; it should not be used to test private members (duh) or member functions. Again, this is acceptable for my application.

There are errors for writing in GCC 4.6.3 because of which he refuses to compile auto member_func() -> decltype(data_member) , but he likes auto member_func() -> char[sizeof data_member] .

+1
source share

All Articles