凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

関数/メンバ関数の最初の引数の型を調べる

はじめに

とある事情である型の引数を持つメンバ関数があるかどうか調べたいと思って次のようなことをつぶやきました。

すると @EzoeRyou さんが光速で実装してくれました。本の虫: ある関数/メンバー関数がある型の仮引数をもっているかどうかを判定するメタプログラム
すごい…。取りあえず自分でも勉強がてら作ってみようと思いました。

コード

自分が使う場面では第1引数しか使わないので @EzoeRyou さんのように汎用化はしないで書きました。代わりに引数が無い場合でも取れるように特殊化してあります。引数の取得には boost::function_types::parameter_types を使用しました。

#include <iostream>
#include <type_traits>
#include <boost/mpl/at.hpp>
#include <boost/function_types/parameter_types.hpp>

template <typename T>
struct first_param
{
	typedef typename boost::mpl::at<
		boost::function_types::parameter_types<T>,
		boost::mpl::int_<0>
	>::type type;
};

template <typename T>
struct first_param<T(*)()>
{
	typedef void type;
};

template <typename T, typename F>
constexpr bool is_first_param(F)
{
	return std::is_same<typename first_param<F>::type, T>::value;
}

template <typename T, typename C, typename R, typename... Types>
constexpr bool is_first_param(R (C::*)(Types...))
{
	typedef R(*F)(Types...);
	return std::is_same<typename first_param<F>::type, T>::value;
}

struct X { void f(double) {} };
void hoge() {}

int main()
{
	std::cout << is_first_param<void>(&hoge) << std::endl;   // 1
	std::cout << is_first_param<int>(&hoge) << std::endl;    // 0
	std::cout << is_first_param<void>(&X::f) << std::endl;    // 0
	std::cout << is_first_param<double>(&X::f) << std::endl; // 1
	return 0;
}

ちなみにオーバーロードした関数がある場合はエラーになります。

で、自分が理想的にやりたかったのはコンストラクタの引数の型の取得です。特定の型を引数に取るコンストラクタがあるかどうか調べる、みたいなことがやりたかったのですが、

std::cout << is_first_param<double>(&X::X) << std::endl;

てな書き方は出来ません。 boost::has_trivial_constructor とかあるし、コンストラクタでも何かしら調べる方法あるんじゃないかなー、と楽観的に調べたのですがどうやら無理なような気がします。
指定した型のコンストラクタを持つ or 指定した型へ暗黙の型変換が可能な型の引数を持つコンストラクタを持つかどうか、なら出来た気がします。
参考: boost - Is there a way to test whether a C++ class has a default constructor (other than compiler-provided type traits)? - Stack Overflow

#include <iostream>
#include <string>

template <typename T, typename U>
class has_ctor_arg_of
{
	template <size_t x>
	class receive_size {};

	template <typename V>
	static int sfinae( receive_size<sizeof V(U())>* );

	template <typename V>
	static char sfinae( ... );

public:
	enum { value = ( sizeof(sfinae<T>(nullptr)) == sizeof(int) ) };
};

struct X { X(std::string) {} };

int main()
{
	std::cout << has_ctor_arg_of<X, std::string>::value << std::endl; // 1
	std::cout << has_ctor_arg_of<X, double>::value      << std::endl; // 0
	std::cout << has_ctor_arg_of<X, float>::value       << std::endl; // 0
	std::cout << has_ctor_arg_of<X, int>::value         << std::endl; // 1
	std::cout << has_ctor_arg_of<X, bool>::value        << std::endl; // 1
	std::cout << has_ctor_arg_of<X, char>::value        << std::endl; // 1
	return 0;
}

うーん、難しい。というか出来無い気がします。

追記(2012/06/10 19:24)

@EzoeRyou さんがコメントに残して下さったように、↑の機能は is_constructible でさっくりできました。

std::cout << std::is_constructible<X, int>::value << std::endl;

ただ、コンストラクタの引数が int か double かの判定とかは無理なんですかね? uum...