C ++组合模板扩展

时间:2017-06-01 03:20:56

标签: c++ templates

我想从库中请求一系列数据,这需要我指定两种类型:

class a;
class b;
class c;
template<typename A, typename B> void get() {....}

void get_all() {
  get<a,a>()
  get<a,b>()
  get<a,c>()
  get<b,a>()
  get<b,b>()
  // etc...
}

我想调用get&lt; A,B&gt;函数用于一组类的每个组合(大约10种不同的变体) - 意味着大约100个不同的调用。我不想手工编写所有这些get()调用。

有没有办法自动生成这些调用?我想我可以使用预处理器宏,但我很好奇是否有办法生成这样的组合列表,给定a,b,c,d,......使用模板代码。

编辑:次要并发症:实际上,这都是成员函数。

template<typename A, typename B> 
void Getter::get() 
{  
  auto assn = fMemberObject->RetrieveAllDataOfType<association<A,B>();
  .. do stuff with assn ...
}

void Getter::get_all() {
  get<a,a>();
}

因此,代码需要处理传递this和模板。 另外,如果可能的话,C ++ 11可能就我想推出语言而言......

编辑2: 结果我想避免重复的案例<a,a><b,b> ......

4 个答案:

答案 0 :(得分:4)

已更新以处理get()未使用相同类型参数调用的新要求,并且get()是类成员函数。我在这里打电话给班级manager

您可以(ab)使用C ++ 11可变参数模板来实现此目的。可能有一种更简洁的方法来做到这一点,但它有效并且(希望)可以理解:

struct manager
{
    // Sample implementation of get(), showing the names of the types.
    template <typename A, typename B>
    void get() {
        std::cout << "A=" << typeid(A).name() << " B=" << typeid(B).name() << '\n';
    }
};

// Implementation, uses variadic templates with recursion.
namespace detail
{
    // Helper to delimit template parameter packs.
    template <typename...> struct pack;

    // Terminating case, <=1 type argument(s) means we are done.
    template <typename...>
    struct call_get_all_beta
    {   
        static void call(manager &) { }
    };

    // Invoke get<First, Second>() and recurse with <First, Tail...>.
    template <typename First, typename Second, typename... Tail>
    struct call_get_all_beta<First, Second, Tail...>
    {   
        static void call(manager &m) {
            m.get<First, Second>();
            call_get_all_beta<First, Tail...>::call(m);
        }
    };

    // Specialization to handle skipping over types that are the same.
    template <typename First, typename... Tail>
    struct call_get_all_beta<First, First, Tail...>
    {   
        static void call(manager &m) {
            call_get_all_beta<First, Tail...>::call(m);
        }
    };

    template <typename...>
    struct call_get_all_alpha;

    // Terminating case, first pack is empty.
    template <typename... B>
    struct call_get_all_alpha<pack<>, pack<B...>>
    {   
        static void call(manager &) { }
    };

    // Pass <FirstA, B...> on to call_get_all_beta, recurse with
    // <pack<TailA...>, pack<B...>>.
    template <typename FirstA, typename... TailA, typename... B>
    struct call_get_all_alpha<pack<FirstA, TailA...>, pack<B...>>
    {   
        static void call(manager &m) {
            call_get_all_beta<FirstA, B...>::call(m);
            call_get_all_alpha<pack<TailA...>, pack<B...>>::call(m);
        }
    };
}

// Helper to call the implementation detail.
template <typename... Types>
void get_all_permutations(manager &m)
{
    detail::call_get_all_alpha<detail::pack<Types...>, detail::pack<Types...>>::call(m);
}

然后我们只做get_all_permutations<a, b, c>();。 (Demo

为解释这是如何工作的,detail::call_get_all_alpha需要两个pack<...>模板参数,两者最初都包含整个类型集。第二个保持不变,但每次递送时,每种类型都从第一个包装上剥离。第一种类型和完整的类型(通过第二包)传递给detail::call_get_all_beta,它使用相同的&#34;剥离&#34;将<A, B, C, D>转换为对get<A, B>()get<A, C>()get<A, D>()的调用的递归技巧。

因此,在更高级别,detail::call_get_all_alpha负责将第一个模板参数迭代到get(),而detail::call_get_all_beta负责迭代第二个模板参数。

答案 1 :(得分:3)

如果对解决模板元编程问题有疑问,可以考虑如何在正常编程中执行此操作。在这种情况下,我们需要类似的东西:

for a in type_list:
    for b in type_list:
        foo(a,b)

所以,让我们直接将它翻译成C ++。我们需要一种表示类型和类型列表的方法:

template <class T> struct tag { };
template <class... Ts> struct type_list { };

我们需要一种迭代类型列表并为每种类型做一些事情的方法。我们称之为函数调用。在C ++ 17中(在C ++ 14及更低版本中,您可以使用swallow / expander技巧在每个f上调用tag<Ts>):

template <class... Ts, class F>
void for_each(type_list<Ts...>, F f) {
    (f(tag<Ts>{}), ...);
}

而那......实际上基本上就是这样。我们需要一个我们实际调用的函数,一个类型列表和代码:

template <class X, class Y>
void foo(tag<X>, tag<Y> ) {
    std::cout << __PRETTY_FUNCTION__ << '\n'; // or something more meaningful
}

int main() {
    type_list<a, b, c> types;

    for_each(types, [=](auto x){
        for_each(types, [=](auto y){
            foo(x, y);
        });});
}

Demo

C++11 version,有点展示了普通lambda的力量。

基于希望类型不同的更新,这也很容易更新。我们可以向tag添加一个等于运算符:

template <class T, class U>
constexpr std::integral_constant<bool, std::is_same<T,U>::value>
operator==(tag<T>, tag<U> ) { return {}; }

template <class T, class U>
constexpr std::integral_constant<bool, !std::is_same<T,U>::value>
operator!=(tag<T>, tag<U> ) { return {}; }

请注意,我们不会返回bool,我们会返回在类型系统中对结果进行编码的类型。然后我们可以自然地使用它:

for_each(types, [=](auto x){
    for_each(types, [=](auto y){
        if constexpr (x != y) {
            foo(x, y);
        }
    });});

foo(x,y)只有在两种标签类型不同时才会被实例化。

答案 2 :(得分:1)

您可以使用以下内容:

class a;
class b;
class c;
template<typename A, typename B> void get() { /*....*/ }

namespace detail {
    template <typename> struct tag{};

    template <typename Tuple, std::size_t...Is>
    void get_all_pairs(tag<Tuple>, std::index_sequence<Is...>)
    {
        constexpr auto size = std::tuple_size<Tuple>::value;
#if 1 // Folding expression with C++17
        (get<std::tuple_element_t<Is / size, Tuple>,
             std::tuple_element_t<Is % size, Tuple>>(), ...);
#else
        const dummy[] = {0, (get<std::tuple_element_t<Is / size, Tuple>,
                                 std::tuple_element_t<Is % size, Tuple>>(),
                             void(), 0)...};
        static_cast<void>(dummy); // Avoid warning for unused variable.
#endif
    }

    template <typename ... Ts>
    void get_all_pairs() {
        get_all_pairs(tag<std::tuple<Ts...>>{},
                      std::make_index_sequence<sizeof...(Ts) * sizeof...(Ts)>{});
    }

}

void get_all() { detail::get_all_pairs<a, b, c>(); }

答案 3 :(得分:1)

如果你可以使用Boost,你可以避免很多基本的类型列表和应用程序样板,从而使代码更容易阅读。无需在这里重新发明轮子。

此示例使用Boost.MPL,它具有广泛的编译器支持,但我确信Boost.Hana中的等效解决方案更漂亮。

首先,生成对的函数(改编自this answer):

#include <utility>

#include <boost/mpl/fold.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/push_back.hpp>

// For each element in TList, append to Accumulator
// the type pair<PairFirst, element>.
template <
    typename TList, typename PairFirst, typename Accumulator = boost::mpl::vector<>>
struct generate_pairs_fixed_element :
    boost::mpl::fold<
        TList,
        Accumulator,
        boost::mpl::push_back<boost::mpl::_1, std::pair<PairFirst, boost::mpl::_2>>>
{};

// For each element in TList, concatenate to Accumulator
// the result of generate_pairs_fixed_element<TList, element>.
template <typename TList, typename Accumulator = boost::mpl::vector<>>
struct generate_pairs_combinations :
    boost::mpl::fold<
        TList,
        Accumulator,
        boost::mpl::lambda<
            generate_pairs_fixed_element<TList, boost::mpl::_2, boost::mpl::_1>
        >>
{};

现在只需将您的类型收集到列表中并生成组合:

class a {};
class b {};
class c {};
class d {};

typedef boost::mpl::vector<a, b, c, d> all_classes_t;

typedef generate_pairs_combinations<all_classes_t>::type all_classes_pairs_t;

随心所欲地做任何事情:

#include <iostream>

#include <boost/mpl/for_each.hpp>
#include <boost/type.hpp>

template <typename A, typename B>
void get() {
    std::cout << "Getting " << typeid(A).name()
              << " and " << typeid(B).name() << std::endl;
}

struct pair_visitor {
    template <typename T, typename U>
    void operator()(boost::type<std::pair<T, U>>) const {
        get<T, U>();
    }
};

int main() {
    boost::mpl::for_each<all_classes_pairs_t, boost::type<boost::mpl::_>>(pair_visitor());
}