考虑以下功能:
template<class F>
void register_handler( F& f ) // any callable object
{
// find out T - the argument type of f
}
这里f
是一个可调用的对象,接受一个参数。它可能是函数指针,std::function
或std::bind
的结果。
问题是,如何确定f
的参数类型并根据该类型执行一些操作?
一个简单的解决方法是明确地将类型添加到模板中,例如
template<class T, class F> // T is the argument type of F
void register_handler( F& f )
但这似乎有点矫枉过正,因为类型F
应该已经包含有关T
类型的必要信息。
假设F
是任何可调用类型,则无法获取其参数类型。考虑一下:
struct callable
{
void operator() (int);
void operator() (float *);
void operator() (std::string const &);
void operator() (std::list<int> &);
};
论证的类型在这里含糊不清。
这个blogpost展示了如何实现一些函数类型特征。这些应该适用于所有可调用的东西(例外:多态仿函数:P)。您可以迭代参数,并使用它们的类型来做一些sfinae或作为额外的模板参数。
从blogpost复制的功能特征:
#include <tuple>
// as seen on http://functionalcpp.wordpress.com/2013/08/05/function-traits/
template<class F>
struct function_traits;
// function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};
template<class R, class... Args>
struct function_traits<R(Args...)>
{
using return_type = R;
static constexpr std::size_t arity = sizeof...(Args);
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
};
};
// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};
// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&,Args...)>
{};
// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};
// functor
template<class F>
struct function_traits
{
private:
using call_type = function_traits<decltype(&F::operator())>;
public:
using return_type = typename call_type::return_type;
static constexpr std::size_t arity = call_type::arity - 1;
template <std::size_t N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename call_type::template argument<N+1>::type;
};
};
template<class F>
struct function_traits<F&> : public function_traits<F>
{};
template<class F>
struct function_traits<F&&> : public function_traits<F>
{};
测试代码:
#include <iostream>
class A
{
};
template <class T>
struct Functor
{
void operator()(const T& t)
{}
};
struct Register
{
//int parameters
template <class T>
static void RegisterFunctor(const T& /*functor*/, typename std::enable_if<std::is_same<typename function_traits<T>::template argument<0>::type, const int&>::value>::type* = 0)
{
std::cout << "Register int func" << std::endl;
}
//A parameters
template <class T>
static void RegisterFunctor(const T& /*functor*/, typename std::enable_if<std::is_same<typename function_traits<T>::template argument<0>::type, const A&>::value>::type* = 0)
{
std::cout << "Register int func" << std::endl;
}
};
void intFunc(const int&) {}
void aFunc(const A&){}
int main(int /*argc*/, char */*argv*/[])
{
Functor<int> intFunctor;
Functor<A> aFunctor;
Register::RegisterFunctor(intFunctor);
Register::RegisterFunctor(&intFunc);
Register::RegisterFunctor(aFunctor);
Register::RegisterFunctor(&aFunc);
return 0;
}
如果F是std::function
you应该能够使用它的member type
并检查`std :: is_same':
template<class F>
void register_handler( F& f ) // any callable object
{
// find out T - the argument type of f
if(std::is_same<int, F::argument_type>::value)
{ .... }
//etc .....
}
一个正在运行的例子here
但是那种代码很快就会成为一个混乱的维护。
您可以使用sfinae并测试您的参数是否可以转换为带有所需参数的std :: function:
#include <type_traits>
#include <functional>
#include <iostream>
class A
{
};
template <class T>
struct Functor
{
void operator()(const T& t)
{}
};
struct Register
{
//int parameters
template <class T>
static void RegisterFunctor(const T& /*functor*/, typename std::enable_if<std::is_constructible<typename std::function<void (int)>, T>::value >::type* = 0)
{
std::cout << "Register int func" << std::endl;
}
//A parameters
template <class T>
static void RegisterFunctor(const T& /*functor*/, typename std::enable_if<std::is_constructible<typename std::function<void (A)>, T>::value >::type* = 0)
{
std::cout << "Register a func" << std::endl;
}
};
void intFunc(int) {}
void aFunc(A){}
int main(int /*argc*/, char */*argv*/[])
{
Functor<int> intFunctor;
Functor<A> aFunctor;
Register::RegisterFunctor(intFunctor);
Register::RegisterFunctor(&intFunc);
Register::RegisterFunctor(aFunctor);
Register::RegisterFunctor(&aFunc);
return 0;
}
在这种情况下,您可以使用一个非常简单的库Boost.Callable Traits。
使用它的例子:
#include <boost/callable_traits.hpp>
#include <iostream>
#include <tuple>
template<typename F>
void register_handler(F&)
{
if constexpr (std::is_same_v<boost::callable_traits::function_type_t<F>, void(int&, double)>)
{
std::cout << "Register handler with syntax void(int&, double)" << std::endl;
}
else if constexpr (std::is_same_v<boost::callable_traits::function_type_t<F>, void(int)>)
{
std::cout << "Register handler with syntax void(int)" << std::endl;
}
}
void func(int&, double)
{}
auto lambda = [](int) {};
int main()
{
{
register_handler(func);
register_handler(lambda);
}
{
using function_type = boost::callable_traits::function_type_t<decltype(func)>;
using expected_function_type = void(int&, double);
std::cout << std::boolalpha << std::is_same_v<expected_function_type, function_type> << std::endl;
}
}
要获得功能类型,您可以使用boost::callable_traits::function_type_t<decltype(func)>
。
正如你在main
和register_handler
函数中看到的那样,可以使用expected_function_type
“function” - > boost::callable_traits::function_type_t<FUNCTION>
来比较std::is_same_v
类型和函数类型(https://en.cppreference.com/w/cpp/types/is_same)
如果你想运行我的例子,请使用例如gcc 7.1.0使用boost 1.66.0和c ++ 17进行编译。 Here你可以在网上做:)