创建一个参数有限的std :: function类型

时间:2016-05-31 09:25:02

标签: c++ templates c++14 variadic-templates std-function

考虑到可调用函数C的类型,我希望在编译时获得std::function;其类型:

  • 具有相同的函数返回类型C
  • 参数类型是函数N
  • 的第一个C参数类型

这意味着,对于给定类型void(int, char, double)和给定N,函数的类型为:

  • N = 1 =&gt;结果类型:std::function<void(int)>
  • N = 2 =&gt;结果类型:std::function<void(int, char)>
  • N = 3 =&gt;结果类型:std::function<void(int, char, double)>
  • N > 3 =&gt;编译时错误

示例:

template<std::size_t N, typename R, typename... A>
constexpr auto get() {
    return /*(magically somehow)*/ std::function<R(FirstNFromA...)>
}

template<std::size_t N, typename R, typename... A>
struct S {
    using func = decltype(get<N, R, A...>());
};

4 个答案:

答案 0 :(得分:6)

它遵循一个可能的解决方案:

#include <tuple>
#include <utility>
#include <functional>
#include <type_traits>

template<
    typename R,
    typename... A,
    std::size_t... I,
    std::enable_if_t<(sizeof...(I)<=sizeof...(A))>* = nullptr
> constexpr auto
get(std::integer_sequence<std::size_t, I...>) {
    return std::function<R(std::tuple_element_t<I, std::tuple<A...>>...)>{};
}

template<std::size_t, typename>
struct FuncType;

template<std::size_t N, typename R, typename... A>
struct FuncType<N, R(A...)> {
    using Type = decltype(get<R, A...>(std::make_index_sequence<N>{}));
};

int main() {
    static_assert(
        std::is_same<
            FuncType<2, void(int, char, double, int)>::Type,
            std::function<void(int, char)>
        >::value,
        "!"
    );
}

基本思想是使用tuple和属于标准模板库的那些实用程序(例如,std::tuple_element),将它们与放置在正确位置的包扩展混合使用这就是全部。
为此,我使用constexpr函数返回给定类型的空std::function对象。然后,函数的类型通过decltype获取,并使用别名导出为FuncType对象的类型。
一切都在编译时进行 为了推导出该函数的正确类型,我使用了一个包含std::integer_sequence的众所周知的模式,并通过扩展索引实际解包元组的类型。

答案 1 :(得分:1)

另一种解决方案可能是:

#include <tuple>
#include <utility>
#include <functional>
#include <type_traits>

template <size_t N, class R, class Pack, class ResultPack, class Voider>
struct FuncTypeImpl;

template <size_t N, class R, template <class...> class Pack, class First, class... Args, class... ResultArgs>
struct FuncTypeImpl<N, R, Pack<First, Args...>, Pack<ResultArgs...>,  std::enable_if_t<(N > 0)>>: FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void> {
   using typename FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void>::Type;
};

template <size_t N, class R, template <class...> class Pack, class... Args, class... ResultArgs>
struct FuncTypeImpl<N, R, Pack<Args...>, Pack<ResultArgs...>, std::enable_if_t<(N == 0)>> {
   using Type = std::function<R(ResultArgs...)>;
};

template<std::size_t, typename>
struct FuncType;

template<std::size_t N, typename R, typename... A>
struct FuncType<N, R(A...)> {
    using Type = typename FuncTypeImpl<N, R, std::tuple<A...>, std::tuple<>, void>::Type;
};

int main() {
    static_assert(
        std::is_same<
            FuncType<3, void(int, char, double, int)>::Type,
            std::function<void(int, char, double)>
        >::value,
        "!"
    );
}

修改 还有一个可能更简单(不需要std :: tuple)解决方案:

#include <utility>
#include <functional>
#include <type_traits>

template <class T>
struct ResultOf;

template <class R, class... Args>
struct ResultOf<R(Args...)> {
   using Type = R;
};

template<std::size_t N, class Foo, class ResultFoo = typename ResultOf<Foo>::Type() , class Voider = void>
struct FuncType;

template<std::size_t N, class R, class First, class... Args, class... ResultArgs >
struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N > 0)>>: FuncType<N-1, R(Args...), R(ResultArgs..., First), void> {
};

template<std::size_t N, class R, class First, class... Args, class... ResultArgs >
struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N == 0)>> {
   using Type = std::function<R(ResultArgs...)>;
};

int main() {
    static_assert(
        std::is_same<
            FuncType<3, void(int, char, double*, int)>::Type,
            std::function<void(int, char, double*)>
        >::value,
        "!"
    );
}

答案 2 :(得分:1)

另一种基于元组的解决方案。

也应该使用C ++ 11。

我想它可以简化,但我不知道如何避免使用bool模板参数(我只是学习C ++ 11)。

#include <tuple>
#include <utility>
#include <functional>
#include <type_traits>


template<std::size_t N, bool Z, typename R, typename...>
struct FTH1;

template <typename R, typename... A, typename... B>
struct FTH1<0U, true, R, std::tuple<A...>, B...>
 { using type = decltype(std::function<R(A...)>{}); };

template <std::size_t N, typename R, typename... A, typename B0, typename... B>
struct FTH1<N, false, R, std::tuple<A...>, B0, B...>
 { using type = typename FTH1<N-1U, (N-1U == 0U), R, std::tuple<A..., B0>, B...>::type; };


template <std::size_t N, typename>
struct FuncType;

template<std::size_t N, typename R, typename... A>
struct FuncType<N, R(A...)>
 { using Type = typename FTH1<N, (N == 0), R, std::tuple<>, A...>::type; };



int main() {
    static_assert(
        std::is_same<
            FuncType<2, void(int, char, double, int)>::Type,
            std::function<void(int, char)>
        >::value,
        "!"
    );
}

p.s:抱歉我的英语不好。

答案 3 :(得分:1)

正如我在评论中提到的,如果出现这样的问题,那么我的第一种方法就是改变问题本身!有时候,不要为“棘手的问题”找到“难以解决的”解决方案,而是让问题本身“更简单”更好! 应该永远不需要写FuncType<2, R(X,Y,Z)>::type而不是简单的std::function<R(X,Y)>

以上是我的真实答案。为了解决您的问题作为编码喜悦,我提出了一个简单的基于宏的答案。它不是编译时间,而是在预处理时为您提供所需的类型。

#define F(R, ...) std::function<R(__VA_ARGS__)>  // shorthand for std::function...
#define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__)  // Main type
#define FUNC_0(R, ...) F(R)  // overload for 0 arg
#define FUNC_1(R, _1, ...) F(R, _1)  // overload for 1 arg
#define FUNC_2(R, _1, _2, ...) F(R, _1, _2)  // overload for 2 args
#define FUNC_3(R, _1, _2, _3, ...) F(R, _1, _2, _3)  // overload for 3 args

用法:

int main() {
  static_assert(std::is_same<
    FuncType(2, void, int, char, double, int),  // <--- see usage
    std::function<void(int, char)>
    >::value, "!");  
}

正如您所看到的,使用情况略有变化。我使用FuncType<2, void(int, char, double, int)>::type而不是FuncType(2, void, int, char, double, int)。这是demo

截至目前,FUNC_N宏已重载最多3个参数。对于更多参数,如果我们想要避免复制粘贴,那么我们可以使用简单程序生成头文件:

  std::ofstream funcN("FUNC_N.h"); // TODO: error check for argv & full path for file
  for(size_t i = 0, N = stoi(argv[1]); i < N; ++i)
  {
    funcN << "#define FUNC_" << i << "(R";  // FUNC_N
    for(size_t j = 1; j <= i; ++j)
      funcN << ", _" << j;  // picking up required args
    funcN << ", ...) ";  // remaining args

    funcN << "F(R";  // std::function
    for(size_t j = 1; j <= i; ++j)
      funcN << ", _" << j;  // passing only relevant args
    funcN <<")\n";
  }

简单地#include

#define F(R, ...) std::function<R(__VA_ARGS__)>
#define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__)
#include"FUNC_N.h"

以下是demo如何生成头文件。