std::declval
すべての答えが複雑すぎるように見えるので、 and (GCC 4.8.3)を使用した独自のソリューションを紹介したいと思います。std::enable_if
#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;
}
出力
1 つは int get() を持っています
2 つは int get() を持っています
int get() を持たない
1つはint add(int, int)
2 つは int add(int, int) を持っています
int add(int, int) を持たない
int には 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()");
}
出力
1つはtypedefタイプです
2 つは typedef 型を持っています
typedef 型を持たない
int には typedef 型がありません
1 つは long に変換可能な typedef 型を持っています
2 つには long に変換可能な typedef 型があります
long に変換可能な typedef 型がありません
int には long に変換可能な typedef 型がありません
1つは typedef type = int です
2 つは typedef type = int を持っています
typedef type = int を持たない
int には typedef type = int がありません
typedef type = long を持たないもの
2つはtypedef type = longを持っていません
typedef type = long を持たない
int には typedef type = long がありません
-------------------------------------------------- ----------
1つはvar xを持っています
2 には var x があります
var x を持たない
int には var x がありません
1 つは long に変換可能な型の var x を持ちます
2 には long に変換可能な型の var x があります
long に変換可能な型の var x がありません
int には long に変換可能な型の var x がありません
1 つは int 型の var x を持ちます
2 には int 型の var x があります
int 型の var x を持たない
int には int 型の var x がありません
long 型の var x がありません
Two には long 型の var x がありません
long 型の var x がありません
int には long 型の var x がありません
-------------------------------------------------- ----------
値が true の var v がある
Two には真に等しい値を持つ var v があります
値が true の var v を持たない
int には、値が true に等しい var v がありません
値が true の var z がない
Two には、値が true の var z がありません
値が true の var z を持たない
int には、値が true の var z がありません
値が false の var v がない
Two には、値が false の var v がありません
値が false の var v を持たない
int には、値が false の var v がありません
値が 1 の var v を持つもの
Two には、値が 1 に等しい var v があります
値が 1 の var v を持たない
int には、値が 1 に等しい var v がありません
int 型の 1 に等しい値を持つ var v がない
Two には、int 型の 1 に等しい値を持つ var v がありません
int 型の 1 に等しい値を持つ var v を持たない
int には、int 型の 1 に等しい値を持つ var v がありません
-------------------------------------------------- ----------
get() がある
2 つが get() を持っています
get() がありません
int には get() がありません
戻り値の型を long に変換できる get() がある
2 つには get() があり、戻り値の型を long に変換できます
戻り値の型を long に変換できる get() がありません
int には、戻り値の型を long に変換できる get() がありません
1 つは、2 つの int を受け入れて ~ long を返す add() です。
Two には、2 つの int を受け入れて ~ long を返す add() があります。
2 つの int を受け入れて ~ long を返す add() がありません
int には、2 つの int を受け入れて ~ long を返す add() がありません
1 つは int get() を持っています
2 つは int get() を持っています
int get() を持たない
int には int get() がありません
長い get() がありません
2 つには長い get() がありません
長い get() がありません
int には長い get() がありません