获取模板可调用对象的参数类型

时间:2014-03-25 09:50:55

标签: c++ templates c++11 template-meta-programming

考虑以下功能:

template<class F>
void register_handler( F& f ) // any callable object
{
   // find out T - the argument type of f
}

这里f是一个可调用的对象,接受一个参数。它可以是函数指针,std::functionstd::bind的结果。

问题是,如何确定f的参数类型并根据该类型执行某些操作?


一个简单的解决方法是明确地将类型添加到模板,例如

template<class T, class F> // T is the argument type of F
void register_handler( F& f )

但这似乎有点过分,因为类型F应该已经包含有关类型T的必要信息。

5 个答案:

答案 0 :(得分:14)

假设F是任何可调用类型,则无法获取其参数类型。考虑一下:

struct callable
{
    void operator() (int);
    void operator() (float *);
    void operator() (std::string const &);
    void operator() (std::list<int> &);
};

这里的论证类型含糊不清。

答案 1 :(得分:5)

这个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>
{};

Testcode:

#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;
}

答案 2 :(得分:1)

如果F是std::function,您应该能够使用其member type并查看`std :: is_same&#39;:

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

但是这种代码很快就会变成一团糟。

答案 3 :(得分:0)

你可以使用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;
}

答案 4 :(得分: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)>

mainregister_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您可以在线进行:)