目標に対して妥協点まではきた気がする
僕よりも優れた先人のサンプルコード
を参考に、
- メンバ関数の有無のみを確認する
- メンバ変数は除外
- いろいろな引数の関数に対応する
を量産するマクロを作成してみました。
変数を除外したのは、変数と関数を区別したかったためです。
妥協点までいったような、というコード全容はこちらです。
#include <iostream>
#include <vector>
namespace has_member_function {
#define HAS_MEMBER_FUNCTION( FunctionName, UniqueID, ... ) \
template < typename T > \
class FunctionName ## UniqueID { \
\
template< typename Arg > \
static auto impl( ... ) -> std :: false_type; \
\
template < typename Arg > \
static auto impl( Arg* ) -> \
decltype( \
std :: declval < Arg >() . FunctionName( __VA_ARGS__ ) \
, std :: true_type() \
); \
\
public: \
static constexpr bool value = decltype( impl< T >(nullptr) ) :: value ; \
};
HAS_MEMBER_FUNCTION( size,, ); // check is T.size() exist
HAS_MEMBER_FUNCTION( begin,, ); // check is T.begin() exist
HAS_MEMBER_FUNCTION( end,, ); // check is T.end() exist
HAS_MEMBER_FUNCTION( empty,, ); // check is T.empty() exist
}
void print( const bool value ){
std :: cout << std :: boolalpha << value << std :: endl;
}
struct A { };
struct B { };
struct C {
void fx ( const A&, const B& );
};
struct D {
int func;
};
namespace has_member_function{
HAS_MEMBER_FUNCTION( at, , 0 );
HAS_MEMBER_FUNCTION( fx, void, );
HAS_MEMBER_FUNCTION( fx, A_B, A(), B() );
}
int main()
{
print( has_member_function :: size < std :: vector< int > > :: value );
print( has_member_function :: begin< std :: vector< int > > :: value );
print( has_member_function :: end < std :: vector< int > > :: value );
print( has_member_function :: empty< std :: vector< int > > :: value );
print( has_member_function :: at < std :: vector< int > > :: value );
print( has_member_function :: fxA_B < C > :: value );
print( has_member_function :: fxvoid < D > :: value );
return 0;
}
ざっくりマクロ解説
例えば
namespace has_member_function{
HAS_MEMBER_FUNCTION(size, , );
}
は
namespace has_member_function {
template < typename T >
class size {
template< typename Arg >
static auto impl( ... ) -> std :: false_type;
template < typename Arg >
static auto impl( Arg* ) ->
decltype(
std :: declval < Arg >() . size( )
, std :: true_type()
);
public:
static constexpr bool value = decltype( impl< T >(nullptr) ) :: value ;
};
}
と展開され、 size()
の有無を確認したいクラス C
に対して
has_member_function :: size<C> :: value
と利用することができます。
マクロの詳細
作成したマクロは
HAS_MEMBER_FUNCTION(FunctionName, UniqueID, ...)
と2つ以上の引数をとります。
FunctionName
確認したいメンバ関数名です。同時に生成するクラス名の一部になります。
UniqueID
オーバーロード等、同名のメンバ関数を区別するための識別子として利用します。
- 検証するクラス
C
- 検証するメンバ関数
test_func()
- 生成する際のマクロ
HAS_MEMBER_FUNCTION(test_func, 01, )
とした場合、生成されるメタ関数、及びそれを利用する方法は
test_func01<C> :: value // check C :: test_func()
となります。
...
検証するメンバ関数の引数を UniqueID
以降に記述します。
サンプルコードでは
HAS_MEMBER_FUNCTION( at, , 0 ); // example -> std :: vector<...>.at(...)
HAS_MEMBER_FUNCTION( fx, A_B, A(), B() ); // example -> check C.fx(...)
このように利用しています。
ここまでの変数を用いてメンバ関数を検証しているのは
decltype( \
std :: declval < Arg >() . FunctionName( __VA_ARGS__ ) \ //<- Here
, std :: true_type() \
); \
この部分になります。fx(const A&, const B&)
メンバ関数を検証するメタ関数を HAS_MEMBER_FUNCTION( fx, A_B, A(), B() );
で生成すると、各引数が展開され
decltype(
std :: declval < Arg >().fx( A(), B() ) //<- Here
, std :: true_type()
);
と変化します。
動作サンプルと実行結果
std :: vector<int>
型や自作の型をサンプルにメンバ関数の検証をしました。
検証用メタ関数生成
namespace has_member_function {
HAS_MEMBER_FUNCTION( size,, ); // check is T.size() exist
HAS_MEMBER_FUNCTION( begin,, ); // check is T.begin() exist
HAS_MEMBER_FUNCTION( end,, ); // check is T.end() exist
HAS_MEMBER_FUNCTION( empty,, ); // check is T.empty() exist
}
namespace has_member_function{
HAS_MEMBER_FUNCTION( at, , 0 );
HAS_MEMBER_FUNCTION( fx, void, );
HAS_MEMBER_FUNCTION( fx, A_B, A(), B() );
}
検証用クラス宣言
struct A { };
struct B { };
struct C {
void fx ( const A&, const B& );
};
struct D {
int func;
};
プリントデバッグ用関数
void print( const bool value ){
std :: cout << std :: boolalpha << value << std :: endl;
}
main
関数
int main()
{
print( has_member_function :: size < std :: vector< int > > :: value );
print( has_member_function :: begin< std :: vector< int > > :: value );
print( has_member_function :: end < std :: vector< int > > :: value );
print( has_member_function :: empty< std :: vector< int > > :: value );
print( has_member_function :: at < std :: vector< int > > :: value );
print( has_member_function :: fxA_B < C > :: value );
print( has_member_function :: fxvoid < D > :: value );
return 0;
}
実行結果
Start
true
true
true
true
true
true
false
0
Finish
ここまでの右往左往
std :: function
に代入できるかで判定するというのも考えました。が、
std :: vector<...>.at(...)
の引数とか戻り値とかを調べたり記述したりするとすごく冗長になってしまう気がしたので頓挫しました。
// でも at
の引数に 0
渡して確認ってうーーーーん。
メンバに対するアドレスを取得する方法だとメンバ変数も入ってしまうのでこれも除外しました。
8/16 2:16 JST
あ、std :: is_function
...
でも引数の型を比べれないのか。
終わりに
もっとスマートな方法、もっとクレイジーな方法あると思いますので、思いついたら方はコメントであるとか記事を作っていただけたらと思います。