2013-02-18 5 views
7

Sto lavorando su una classe di mappa ricorsiva denominata group_by che modella l'omonimo SQL.Come scrivere un metodo variadic che sostituisce le chiamate al metodo concatenato?

Ad esempio, gb è un oggetto group_by che memorizza puntatori a foo raggruppati per std::string, int e char tipi di chiavi, in questo ordine.

group_by<foo,std::string,int,char> gb; 

group_by fornisce un metodo at(I const& key) di accesso che può essere utilizzato per guardare dentro mappa livello attuale. Concatenare le chiamate at() per recuperare mappe più profonde funziona correttamente.

auto& v = gb.at(k1).at(k2).at(k3).get_vec(); 

PROBLEMA

vorrei creare un'alternativa di at() chiamato at_variadic(Args const& ...args) che può recuperare le mappe più profondi tutto in una sola chiamata, senza concatenamento.

auto& w = gb.at_variadic(k1, k2); 
auto& x = gb.at_variadic(k1, k2, k3); 

Tuttavia, io sono in esecuzione in alcuni problemi. Prima di tutto, non so come specificare il tipo di ritorno, poiché dipende dagli argomenti variadici. Forse usare decltype(), in qualche modo?

RISPOSTA LAVORO

Ecatmur's answer below delineato un buon approccio.

Ho dovuto giocare con il caso terminale di group_by<> per rendere il compilatore felice, ma il codice sottostante, basato pesantemente sulla risposta di Ecatmur, sembra funzionare correttamente con gcc 4.7.2.

#include <cassert> 
#include <map> 
#include <vector> 
#include <iostream> 

template< typename T, typename... Args > 
struct group_by 
{ 
    using child_type = T; 

    std::vector<T*> m_vec; 

    void insert(T* t) 
    { 
     m_vec.push_back(t); 
    } 

    child_type& 
    at(size_t i) 
    { 
     return *m_vec[i]; 
    } 
}; 

template< typename T, typename I, typename... Args > 
struct group_by<T,I,Args...> 
{ 
    using child_type = group_by<T,Args...>; 

    std::map<I,child_type> m_map; 

    void insert(T* t) 
    { 
     m_map[ *t ].insert(t); 
    } 

    child_type& at(I const& key) 
    { 
    return m_map.at(key); 
    } 

    template<typename... Ks> 
    auto 
    at(I const& i, Ks const&...ks) 
    -> decltype(m_map.at(i).at(ks...)) 
    { 
     return m_map.at(i).at(ks...); 
    } 
}; 

// ----------------------------------------------------------------------------- 

struct foo 
{ 
    std::string s; 
    int   i; 
    char   c; 

    operator std::string() const { return s; } 
    operator int  () const { return i; } 
    operator char  () const { return c; } 

    bool operator==(foo const& rhs) const 
    { 
     return s==rhs.s && i==rhs.i && c==rhs.c; 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    foo f1{ "f1", 1, 'z' }; 
    foo f2{ "f2", 9, 'y' }; 
    foo f3{ "f3", 3, 'x' }; 
    foo f4{ "f1", 4, 'k' }; 

    group_by<foo,std::string,int,char> gb; 

    gb.insert(&f1); 
    gb.insert(&f2); 
    gb.insert(&f3); 
    gb.insert(&f4); 

    std::string k1{ "f1" }; 
    int   k2{ 1 }; 
    char  k3{ 'z' }; 

    auto& a = gb.at(k1).at(k2).at(k3).at(0); 
    auto& b = gb.at(k1).at(k2).m_map; 
    auto& c = gb.at(k1).m_map; 
    auto& d = gb.at(k1, k2).m_map; 
    auto& e = gb.at(k1, k2, k3).m_vec; 
    auto& f = gb.at(k1, k2, k3, 0); 

    assert(a==f1); 
    assert(b.size()==1); 
    assert(c.size()==2); 
    assert(d.size()==1); 
    assert(e.size()==1); 
    assert(f==f1); 

    return 0; 
} 

risposta

4

chiamate di metodo concatenate sono essenzialmente ricorsivo, quindi è necessario implementare at ricorsivo:

child_type& at(I const& key) { 
    return m_map.at(key); 
} 

template<typename J, typename... Ks> 
auto at(const I &i, const J &j, const Ks &...ks) 
-> decltype(m_map.at(i).at(j, ks...)) { 
    return m_map.at(i).at(j, ks...); 
} 

notare che, poichè at richiede almeno 1 argomento, la forma variadic richiede almeno 2 parametri. Questo è molto più semplice da implementare rispetto alla spedizione su sizeof... e dovrebbe essere più facile da leggere.

+0

+1 tyvm - proverò questo fuori adesso – kfmfe04

+0

Hai provato questo? Non sembra compilare ... –

+0

@AndyProwl hai ragione - ho dovuto giocarci per farlo funzionare. Ripuliscilo adesso. Aggiornerà OP quando fatto. La sua risposta è stata sufficiente per farcela. – kfmfe04