2014-10-08 24 views
5

निम्नलिखित कोड काम करता है:अन्य पैक में घोंसला वाला टेम्पलेट पैक कैसे वापस करें?

#include <iostream> 
#include <list> 

struct Base {}; 
struct A : Base {}; struct B : Base {}; struct C : Base {}; 
struct D : Base {}; struct E : Base {}; struct F : Base {}; 

template <int KEY, typename... RANGE> struct Map {}; // one-to-many map (mapping KEY to RANGE...) 

template <typename...> struct Data {}; 

using Database = Data< Map<0, A,B,C>, Map<1, D,E,F> >; 

template <int N, typename FIRST, typename... REST> // N has meaning in my program, but not shown here. 
void insertInMenu (std::list<Base*>& menu) { 
    menu.push_back(new FIRST); 
    insertInMenu<N, REST...> (menu); 
} 

template <int N> 
void insertInMenu (std::list<Base*>&) {} // End of recursion. 

template <int N> 
std::list<Base*> menu() { 
    std::list<Base*> m; 
    insertInMenu<0, A,B,C>(m); // A,B,C should be obtained using N and Database. 
    return m; 
} 

int main() { 
    std::list<Base*> m = menu<0>(); 
    std::cout << "m.size() = " << m.size() << std::endl; // 3 
} 

लेकिन जैसा कि ऊपर मेरी टिप्पणी में संकेत दिया है, मैं Database और मूल्य N उपयोग करने के लिए सीमा A,B,C (या D,E,F) या जो कुछ भी प्राप्त करने के लिए चाहते हैं। लेकिन मुझे नहीं पता कि यह कैसे करना है? क्या कोई मदद कर सकता है? लाइन

insertInMenu<0, A,B,C>(m); 

तरह

obtainRange<Database, N>() 

कुछ के साथ प्रतिस्थापित किया जाएं, क्योंकि वे संकलन समय ज्ञात मानों रेंज मैं चाहता हूँ प्राप्त करने के लिए पर्याप्त जानकारी होना चाहिए की जरूरत है।

obtainRange<Database, 0>() 

लौटना चाहिए A,B,C और

obtainRange<Database, 1>() 

इस मामले में D,E,F लौटना चाहिए।

उत्तर

1
template <typename D, int N> 
struct obtainRange; 

template <int N, typename... Ts, typename... Maps> 
struct obtainRange<Data<Map<N, Ts...>, Maps...>, N> 
{ 
    using type = std::tuple<Ts...>; 
}; 

template <int N, int M, typename... Ts, typename... Maps> 
struct obtainRange<Data<Map<M, Ts...>, Maps...>, N> 
    : obtainRange<Data<Maps...>, N> {}; 

template <int N, typename Tuple, std::size_t... Is> 
std::list<Base*> menu(std::index_sequence<Is...>) 
{ 
    std::list<Base*> m; 
    insertInMenu<0, typename std::tuple_element<Is, Tuple>::type...>(m); 
    return m; 
} 

template <int N> 
std::list<Base*> menu() 
{  
    using Tuple = typename obtainRange<Database, N>::type; 
    return menu<N, Tuple>(std::make_index_sequence<std::tuple_size<Tuple>::value>{}); 
} 

DEMO


आप उपयोग नहीं कर सकते सी ++ 14 के index_sequence, तो नीचे एक विकल्प सी ++ 11-संगतता दिया गया है:

template <std::size_t... Is> 
struct index_sequence {}; 

template <std::size_t N, std::size_t... Is> 
struct make_index_sequence_h : make_index_sequence_h<N - 1, N - 1, Is...> {}; 

template <std::size_t... Is> 
struct make_index_sequence_h<0, Is...> 
{ 
    using type = index_sequence<Is...>; 
}; 

template <std::size_t N> 
using make_index_sequence = typename make_index_sequence_h<N>::type; 

आप आगे जा सकते हैं, और Data औरके समान मनमाने ढंग से टेम्पलेट्स के साथ काम कर सकते हैं, उदा। एक std::tuple (Data के बजाय) Map एस के, टेम्पलेट टेम्पलेट-पैरामीटर का उपयोग कर:

template <typename D, int N> 
struct obtainRange; 

template <template <typename...> class DB 
     , template <int, typename...> class MP 
     , typename... Ts 
     , typename... Maps 
     , int N> 
struct obtainRange<DB<MP<N, Ts...>, Maps...>, N> 
{ 
    using type = std::tuple<Ts...>; 
}; 

template <template <typename...> class DB 
     , template <int, typename...> class MP 
     , typename... Ts 
     , typename... Maps 
     , int M 
     , int N> 
struct obtainRange<DB<MP<M, Ts...>, Maps...>, N> : obtainRange<DB<Maps...>, N> {}; 

DEMO 2

1
// There is no need to take the length here, btw. 
template <int, typename... Args> 
void insertInMenu (std::list<Base*>& menu) 
{ 
    // Non-recursive push_backs: 
    std::initializer_list<int>{ (menu.push_back(new Args), 0)... }; 
} 

template <int, typename> struct InsertEnv; 

template <int key, int otherKey, typename... Args, typename... Rest> 
struct InsertEnv<key, Data<Map<otherKey, Args...>, Rest...>> : 
    InsertEnv<key, Data<Rest...>> {}; 

template <int key, typename... Args, typename... Rest> 
struct InsertEnv<key, Data<Map<key, Args...>, Rest...>> 
{ 
    void operator()(std::list<Base*>& menu) 
    { 
     insertInMenu<key, Args...> (menu); 
    } 

    std::list<Base*> operator()() 
    { 
     return {new Args...}; 
    } 
}; 

template <int N> 
void addToMenu (std::list<Base*>& menu) 
{ 
    InsertEnv<N, Database>()(menu); 
} 

template <int N> 
std::list<Base*> menu() 
{ 
    return InsertEnv<N, Database>()(); 
} 

उपयोगकर्ता या तो के रूप में

menu<N>() // list with the desired elements in it 

या

std::list<Base*> list; 
addToMenu<N>(list); // pushes back the desired elements 

Demo के रूप में।

+0

जहां 'Database' से एक' Map' चयन कर रहे हैं इसके सूचकांक के आधार पर? –

+0

@PiotrS। टाइपो। फिक्स्ड। – Columbo

+0

मैं मतलब है कि 'insertInMenu <1, डाटा , मानचित्र <1,C,D>>', 'सी, D' (नक्शा' Database' में अनुक्रमित '1' के रूप में) के साथ परिणाम चाहिए का विस्तार नहीं' मानचित्र <0,AB>, मानचित्र <1,C,D> ' –

1

Live at Coliru

template <typename, int> 
struct obtainRange {}; 

template <int N, typename...Types, typename...Rest> 
struct obtainRange<Data<Map<N, Types...>, Rest...>, N> : 
    Data<Types...> {}; 

template <int N, typename T, typename...Rest> 
struct obtainRange<Data<T, Rest...>, N> : 
    obtainRange<Data<Rest...>, N> {}; 

template <typename...Types> 
std::list<Base*> menu(Data<Types...>) { 
    return { new Types{}... }; 
} 

template <int N> 
std::list<Base*> menu() { 
    return menu(obtainRange<Database, N>{}); 
} 
+0

यह छोटा समाधान std :: सूची के साथ पूरी तरह से काम करता है, लेकिन मेरा कंटेनर वास्तव में एक विशेष वर्ग है इसलिए मुझे उस विशेष कंटेनर को संभालने के लिए अभी भी insertInMenu का उपयोग करने की आवश्यकता है।लेकिन एसटीएल कंटेनर के विशेष मामले के लिए एक अच्छा त्वरित समाधान देखना अच्छा होता है। – prestokeys

+0

@prestokeys यह बिल्कुल एक विशेष मामला नहीं है - यह किसी भी कंटेनर के साथ काम करेगा जिसे 'बेस *' की एक ब्रैस्ड-इनिट-सूची से बनाया जा सकता है - लेकिन जब आप 'unique_ptr ' को प्रतिस्थापित करते हैं तो निष्पक्षता में एक वास्तविक कार्यक्रम में अलग हो जाता है 'बेस * 'चूंकि आप तत्वों को' std :: startizer_list' से नहीं ले जा सकते हैं। – Casey

संबंधित मुद्दे