由于所有的答案对我来说都太复杂了,我想介绍一下我自己的解决方案
std::declval
和
std::enable_if
(GCC 4.83)
#define MEMBER_FUNC_CHECKER(name, fn, ret, args) \
template<class C, typename=void> struct name : std::false_type {}; \
template<class C> struct name<C, typename std::enable_if< \
std::is_convertible<decltype(std::declval<C>().fn args), ret \
>::value>::type> : std::true_type {};
注意:这不是对签名的精确检查,而是对带有可转换返回类型的可调用函数的精确检查。
(编辑:更改自
is_same
到
is_convertible
)
试验
struct One {
int get() { return 0; }
int add(int x, int y) { return x+y; }
};
struct Two: One {};
struct Not {};
MEMBER_FUNC_CHECKER(has_get, get, int, ())
MEMBER_FUNC_CHECKER(has_add, add, int, (1,2))
int main() {
cout << "One " << (has_get<One>() ? "has" : "does not have")
<< " int get()" << endl;
cout << "Two " << (has_get<Two>() ? "has" : "does not have")
<< " int get()" << endl;
cout << "Not " << (has_get<Not>() ? "has" : "does not have")
<< " int get()" << endl;
cout << "One " << (has_add<One>() ? "has" : "does not have")
<< " int add(int, int)" << endl;
cout << "Two " << (has_add<Two>() ? "has" : "does not have")
<< " int add(int, int)" << endl;
cout << "Not " << (has_add<Not>() ? "has" : "does not have")
<< " int add(int, int)" << endl;
cout << "int " << (has_get<int>() ? "has" : "does not have")
<< " int get()" << endl;
}
产量
One has int get()
Two has int get()
Not does not have int get()
One has int add(int, int)
Two has int add(int, int)
Not does not have int add(int, int)
int does not have int get()
更新:我的支票
/// Checker for typedef with given name and convertible type
#define TYPEDEF_CHECKER(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
std::is_convertible<typename C::name, T>::value>::type> : std::true_type {}
/// Checker for typedef with given name and exact type
#define TYPEDEF_CHECKER_STRICT(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
std::is_same<typename C::name, T>::value>::type> : std::true_type {}
/// Checker for typedef with given name and any type
#define TYPEDEF_CHECKER_ANY(checker, name) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
!std::is_same<typename C::name*, void>::value>::type> : std::true_type {}
/// Checker for member with given name and convertible type
#define MTYPE_CHECKER(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
std::is_convertible<decltype(C::name), T>::value>::type> : std::true_type {}
/// Checker for member with given name and exact type
#define MTYPE_CHECKER_STRICT(checker, name) \
template<class C, typename T, typename = void> struct checker : std::false_type {}; \
template<class C, typename T> struct checker<C, T, typename std::enable_if< \
std::is_same<decltype(C::name), T>::value>::type> : std::true_type {}
/// Checker for member with given name and any type
#define MTYPE_CHECKER_ANY(checker, name) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
!std::is_same<decltype(C::name)*, void>::value>::type> : std::true_type {}
/// Checker for static const variable with given name and value
#define MVALUE_CHECKER(checker, name, val) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
std::is_convertible<decltype(C::name), const decltype(val)>::value && C::name == val>::type> : std::true_type {}
/// Checker for static const variable with given name, value and type
#define MVALUE_CHECKER_STRICT(checker, name, val) \
template<class C, typename = void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
std::is_same<decltype(C::name), const decltype(val)>::value && C::name == val>::type> : std::true_type {}
/// Checker for member function with convertible return type and accepting given arguments
#define METHOD_CHECKER(checker, name, ret, args) \
template<class C, typename=void> struct checker : std::false_type {}; \
template<class C> struct checker<C, typename std::enable_if< \
std::is_convertible<decltype(std::declval<C>().name args), ret>::value>::type> : std::true_type {};
/// Checker for member function with exact retutn type and accepting given arguments
#define METHOD_CHECKER_STRICT_RET(name, fn, ret, args) \
template<class C, typename=void> struct name : std::false_type {}; \
template<class C> struct name<C, typename std::enable_if< \
std::is_same<decltype(std::declval<C>().fn args), ret>::value>::type> : std::true_type {};
/// Checker for member function accepting given arguments
#define METHOD_CHECKER_ANY(name, fn, args) \
template<class C, typename=void> struct name : std::false_type {}; \
template<class C> struct name<C, typename std::enable_if< \
!std::is_same<decltype(std::declval<C>().fn args)*, void>::value>::type> : std::true_type {};
测试代码
struct One {
typedef int type;
static constexpr bool v = true;
type x;
One(type x = 0): x(x) {}
~One() {}
type get() { return x; }
type add(type x, type y) { return x+y; }
};
struct Two: One {};
struct Not {};
TYPEDEF_CHECKER(has_type, type);
TYPEDEF_CHECKER_ANY(any_type, type);
TYPEDEF_CHECKER_STRICT(exact_type, type);
MTYPE_CHECKER(has_x, x);
MTYPE_CHECKER_ANY(any_x, x);
MTYPE_CHECKER_STRICT(exact_x, x);
MVALUE_CHECKER(true_v, v, true);
MVALUE_CHECKER(true_z, z, true);
MVALUE_CHECKER(false_v, v, false);
MVALUE_CHECKER(one_v, v, 1);
MVALUE_CHECKER_STRICT(exact_v, v, 1);
METHOD_CHECKER(has_get, get, long, ());
METHOD_CHECKER(has_add, add, long, (1,2))
METHOD_CHECKER_ANY(any_get, get, ());
METHOD_CHECKER_STRICT_RET(int_get, get, int, ())
METHOD_CHECKER_STRICT_RET(long_get, get, long, ())
int main() {
#define CHECK(name, desc, ...) cout << endl; \
cout << "One " << (name<One, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl; \
cout << "Two " << (name<Two, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl; \
cout << "Not " << (name<Not, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl; \
cout << "int " << (name<int, ##__VA_ARGS__>() ? "has " : "does not have ") << desc << endl
string sep = string(60, '-');
cout << sep;
CHECK(any_type, "typedef type");
CHECK(has_type, "typedef type convertible to long", long);
CHECK(exact_type, "typedef type = int", int);
CHECK(exact_type, "typedef type = long", long);
cout << sep;
CHECK(any_x, "var x");
CHECK(has_x, "var x of type convertible to long", long);
CHECK(exact_x, "var x of type int", int);
CHECK(exact_x, "var x of type long", long);
cout << sep;
CHECK(true_v, "var v with value equal to true");
CHECK(true_z, "var z with value equal to true");
CHECK(false_v, "var v with value equal to false");
CHECK(one_v, "var v with value equal to 1");
CHECK(exact_v, "var v with value equal to 1 of type int");
cout << sep;
CHECK(has_get, "get()");
CHECK(has_get, "get() with return type covertible to long");
CHECK(has_add, "add() accepting two ints and returning ~ long");
CHECK(int_get, "int get()");
CHECK(long_get, "long get()");
}
产量
One has typedef type
Two has typedef type
Not does not have typedef type
int does not have typedef type
One has typedef type convertible to long
Two has typedef type convertible to long
Not does not have typedef type convertible to long
int does not have typedef type convertible to long
One has typedef type = int
Two has typedef type = int
Not does not have typedef type = int
int does not have typedef type = int
One does not have typedef type = long
Two does not have typedef type = long
Not does not have typedef type = long
int does not have typedef type = long
------------------------------------------------------------
One has var x
Two has var x
Not does not have var x
int does not have var x
One has var x of type convertible to long
Two has var x of type convertible to long
Not does not have var x of type convertible to long
int does not have var x of type convertible to long
One has var x of type int
Two has var x of type int
Not does not have var x of type int
int does not have var x of type int
One does not have var x of type long
Two does not have var x of type long
Not does not have var x of type long
int does not have var x of type long
------------------------------------------------------------
One has var v with value equal to true
Two has var v with value equal to true
Not does not have var v with value equal to true
int does not have var v with value equal to true
One does not have var z with value equal to true
Two does not have var z with value equal to true
Not does not have var z with value equal to true
int does not have var z with value equal to true
One does not have var v with value equal to false
Two does not have var v with value equal to false
Not does not have var v with value equal to false
int does not have var v with value equal to false
One has var v with value equal to 1
Two has var v with value equal to 1
Not does not have var v with value equal to 1
int does not have var v with value equal to 1
One does not have var v with value equal to 1 of type int
Two does not have var v with value equal to 1 of type int
Not does not have var v with value equal to 1 of type int
int does not have var v with value equal to 1 of type int
------------------------------------------------------------
One has get()
Two has get()
Not does not have get()
int does not have get()
One has get() with return type covertible to long
Two has get() with return type covertible to long
Not does not have get() with return type covertible to long
int does not have get() with return type covertible to long
One has add() accepting two ints and returning ~ long
Two has add() accepting two ints and returning ~ long
Not does not have add() accepting two ints and returning ~ long
int does not have add() accepting two ints and returning ~ long
One has int get()
Two has int get()
Not does not have int get()
int does not have int get()
One does not have long get()
Two does not have long get()
Not does not have long get()
int does not have long get()