I\'m asking for a template trick to detect if a class has a specific member function of a given signature.
The problem is similar to the one cited here http://www.go
Here are some usage snippets: *The guts for all this are farther down
Check for member x
in a given class. Could be var, func, class, union, or enum:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x::value;
Check for member function void x()
:
//Func signature MUST have T as template variable here... simpler this way :\
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x::value;
Check for member variable x
:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x::value;
Check for member class x
:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x::value;
Check for member union x
:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x::value;
Check for member enum x
:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x::value;
Check for any member function x
regardless of signature:
CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x::value;
OR
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x::value;
Details and core:
/*
- Multiple inheritance forces ambiguity of member names.
- SFINAE is used to make aliases to member names.
- Expression SFINAE is used in just one generic has_member that can accept
any alias we pass it.
*/
//Variadic to force ambiguity of class members. C++11 and up.
template struct ambiguate : public Args... {};
//Non-variadic version of the line above.
//template struct ambiguate : public A, public B {};
template
struct got_type : std::false_type {};
template
struct got_type : std::true_type {
typedef A type;
};
template
struct sig_check : std::true_type {};
template
struct has_member {
template static char ((&f(decltype(&C::value))))[1];
template static char ((&f(...)))[2];
//Make sure the member name is consistently spelled the same.
static_assert(
(sizeof(f(0)) == 1)
, "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
);
static bool const value = sizeof(f(0)) == 2;
};
Macros (El Diablo!):
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member) \
\
template \
struct Alias_##member; \
\
template \
struct Alias_##member < \
T, std::integral_constant::value> \
> { static const decltype(&T::member) value; }; \
\
struct AmbiguitySeed_##member { char member; }; \
\
template \
struct has_member_##member { \
static const bool value \
= has_member< \
Alias_##member> \
, Alias_##member \
>::value \
; \
}
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name) \
\
template \
struct has_member_var_##var_name : std::false_type {}; \
\
template \
struct has_member_var_##var_name< \
T \
, std::integral_constant< \
bool \
, !std::is_member_function_pointer::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \
\
template \
struct has_member_func_##templ_postfix : std::false_type {}; \
\
template \
struct has_member_func_##templ_postfix< \
T, std::integral_constant< \
bool \
, sig_check::value \
> \
> : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name) \
\
template \
struct has_member_class_##class_name : std::false_type {}; \
\
template \
struct has_member_class_##class_name< \
T \
, std::integral_constant< \
bool \
, std::is_class< \
typename got_type::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name) \
\
template \
struct has_member_union_##union_name : std::false_type {}; \
\
template \
struct has_member_union_##union_name< \
T \
, std::integral_constant< \
bool \
, std::is_union< \
typename got_type::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name) \
\
template \
struct has_member_enum_##enum_name : std::false_type {}; \
\
template \
struct has_member_enum_##enum_name< \
T \
, std::integral_constant< \
bool \
, std::is_enum< \
typename got_type::type \
>::value \
> \
> : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func) \
template \
struct has_member_func_##func { \
static const bool value \
= has_member_##func::value \
&& !has_member_var_##func::value \
&& !has_member_class_##func::value \
&& !has_member_union_##func::value \
&& !has_member_enum_##func::value \
; \
}
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member) \
CREATE_MEMBER_CHECK(member); \
CREATE_MEMBER_VAR_CHECK(member); \
CREATE_MEMBER_CLASS_CHECK(member); \
CREATE_MEMBER_UNION_CHECK(member); \
CREATE_MEMBER_ENUM_CHECK(member); \
CREATE_MEMBER_FUNC_CHECK(member)