2017-06-01 137 views
2

我想請求一系列從庫,這需要我的數據到指定兩種類型:C++組合模板擴展

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 < A,B >功能的每個組合一組類(約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> ......

+0

我會寫一個腳本,生成的代碼。或者看看設計,看看我能否想出一些不需要我去做所有這些調用的東西。也許如果你告訴我們你試圖用這樣的代碼解決的問題,我們也許可以幫助你提出一個更好的解決方案? ([關於XY問題的相關閱讀](http://xyproblem.info/),你的問題就是一個例子。) –

+4

你對結果做了什麼? ''真的''do_some_action'?因爲帶有'void'返回類型的'get'不是很有意義。 – GManNickG

+0

@GManNickG我懷疑'get()'有副作用,但是從OP的說明會很好。 – cdhowie

回答

2

如果對解決模板元編程問題有疑問,有必要考慮如何在正常編程中完成此任務。在這種情況下,我們想要類似於:

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和下,你可以使用燕子/擴展伎倆每個tag<Ts>調用f):

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)只會被實例化如果兩個標籤類型是不同的。

+0

我試過這個,它似乎編譯..直到我嘗試在foo()函數中做些什麼。看到我上面的編輯:它現在抱怨說'協會'是一個不完整的類型。我不知道爲什麼。編輯出for_each代碼並調用foo ()工作正常,所以我不知道爲什麼。 – Nathaniel

+0

@Nathaniel我不能告訴你爲什麼我看不到的代碼不起作用。可能在你定義某個地方之前使用'association'。但是,這應該允許內部lambda體中的任意代碼 - 假設類型都是在那個點上定義的。 – Barry

+0

原來這是一個潛在的假設,A不與B相同。爲了避免這種情況,重載foo()是微不足道的。 – Nathaniel

4

更新來處理get()不被調用具有相同的類型參數,並且get()是一個類成員函數的新要求。我在這裏打電話給manager


您可以(ab)使用C++ 11 variadic模板來實現此目的。有可能是一個更簡潔的方式來做到這一點,但它的工作原理是(希望)可以理解的:

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,它使用相同的「剝離」遞歸技術將<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

您可以使用類似:

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>(); } 
1

如果升壓是提供給你,你就可以避免很多的基本類型串及應用樣板,導致代碼更容易閱讀。沒有必要在這裏重新發明輪子。

本示例使用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()); 
}