0

I was searching for template code that answers the question "does class T have member X?". There are some solutions on the web that employ SFINAE (for example, see How to detect whether there is a specific member variable in class?), but they all have the disadvantage that the member name X is hard coded in the solution. So I created a macro DECL_HAS_MEMBER(member_name) which declares the required template classes that check for member "member_name":

#define DECL_HAS_MEMBER(member_name) \
template<typename T, typename = void> struct has_member_##member_name : std::false_type {}; \
template<typename T> \
struct has_member_##member_name<T, decltype((void)T::member_name, (void)0)> : std::true_type {}; \
template<typename T> constexpr auto has_member_##member_name##_v = has_member_##member_name<T>::value;

Now I can use the following code:

DECL_HAS_MEMBER(test)
...
bool has_test = has_member_test_v<MYCLASS>;

(Side note: In fact, I refined this code a bit to find out whether "test" is a static integral data member.)

The problem arises when I have two "DECL_HAS_MEMBER(test)" calls in the same translation unit, for example via different included header files. The MSVC compiler issues these error messages:

warning C4348: 'has_member_test': redefinition of default parameter: parameter 2
error C2953: 'has_member_test': class template has already been defined
error C2976: 'has_member_test': too few template arguments
error C2086: 'const auto has_member_test_v': redefinition

While I understand that the compiler doesn't want to see two (although identical) definitions of a template or a variable, I don't see how to solve the problem. An include guard clearly won't help because it is not the header file about which the compiler complains.

Any ideas?

h.s.
  • 139
  • 9
  • You probably want `std::experimental::is_detected`. – HolyBlackCat Mar 10 '19 at 10:00
  • @HolyBlackCat, I have the impression the the MS compiler does not yet support that. Aside from that, I guess you are right... – h.s. Mar 10 '19 at 13:09
  • Put each `DECL_HAS_MEMBER` in a different `detail` namespace? – Oktalist Mar 10 '19 at 13:14
  • @Oktalist, that is a good idea. I played with it - when I use a namespace around _every_ DECL_HAS_MEMBER, it seems to work :-) – h.s. Mar 10 '19 at 13:45
  • You can still rewrite [`std::experimental::is_detected`](https://en.cppreference.com/w/cpp/experimental/is_detected) (and maybe some dependencies (as trivial `std::void_t`)). It is not that long. – Jarod42 Mar 10 '19 at 14:37

1 Answers1

0

There are of course several options. Here are two:

1) The code your macro generates for "test" of course works for any class. Thus you really need it once only. You could define all your DECL_HAS_MEMBER in one extra include file, include it where you want to make checks and do the checks.

2) you do the DECL_HAS_MEMBER inside the class you want to test. In this scenario all checks are in separate class namespaces and there will be no conflicts.

Ralf Ulrich
  • 1,575
  • 9
  • 25
  • 1
    Good thoughts! However... Re 1 - When I place this macro in a header file which many people use, I'd have to add DECL_HAS_MEMBER macros for all thinkable member names... Re 2 - The place where I ask "do you have member X" is in a template declaration like this: template> { struct something {...};}; This leaves no place to put the test into a class. – h.s. Mar 10 '19 at 13:59