2017-03-09 3 views
0

Betrachten wir die folgenden Funktionen (Es nutzt die CSV parser library from ben-strasser (github))Wie schreibe ich eine Variable-Template-Funktion mit Objekten als Parameter?

void col1(const std::string &fn, Base *v0) 
{ 
    io::CSVReader<2> in(fn); 
    in.read_header(io::ignore_extra_column, "epoch", v0->column); 
    double ign; 
    while (in.read_row(ign, v0->value)) { 
     v0->process(); 
    } 
} 

void col2(const std::string &fn, Base *v0, Base *v1) 
{ 
    io::CSVReader<3> in(fn); 
    in.read_header(io::ignore_extra_column, "epoch", v0->column, v1->column); 
    double ign; 
    while (in.read_row(ign, v0->value, v1->value)) { 
     v0->process(); 
     v1->process(); 
    } 
} 

Diese Funktion verarbeitet den Wert in Spalte 2 aus einer CSV-Datei. v0 des Typs Base * enthält das Element value, das von read_row gefüllt wird und in der process -Methode verarbeitet wird. Base ist eine Interface-Klasse von Berechnungsmethoden (zum Beispiel: eine ist Max, eine andere ist MinMaxAvg).

Wie könnte ich diese Funktion umschreiben, um eine beliebige Anzahl von Base * Argumente zu akzeptieren, um mehrere Spalten zu verarbeiten?

read_header und read_row sind variadic-template-Funktion und können somit eine beliebige Anzahl von Argumenten akzeptieren, aber sie funktionieren nur mit Skalaren.

Wie erweitere/entpacke ich das variadic-Argument, so dass es ein Mitglied aufruft oder benutzt?

ich einige Dinge ausprobiert, einige Beispiele zu lesen, aber ich bin nicht in der Lage, etwas zu schaffen, das hier funktioniert, ist, mein Strom/ridicules Code:

template<unsigned int COL> 
void func(const std::string &fn, Base &... values) 
{ 
    io::CSVReader<COL> in(fn); 
    // that's it :-(
} 
+0

@OP: Sollte jeder 'Base' von der Variadic-Packung sei p wie in Ihrem ersten Snippet wie "v0", aber mit einer eigenen Spalte in der Reihenfolge? – Quentin

+0

@Quentin Ja, ich füge meine aktuelle Version von Col2 hinzu. –

+0

Sie wechseln von Zeigern (Base *) zu Referenzen (Base &) - beabsichtigt? – Aconcagua

Antwort

1

Einige gut platzierte Pack Erweiterungen werden Dandy arbeiten:

template <class... Bases> 
void col(const std::string &fn, Bases *... bases) 
{ 
    io::CSVReader<sizeof...(Bases) + 1u> in(fn); 
    in.read_header(io::ignore_extra_column, "epoch", bases->column...); 
    double ign; 
    while (in.read_row(ign, bases->value...)) { 
     // Awful C++11 arbitrary expansion trick 
     int dum[]{ 0, (void(
      bases->process() 
     ), 0)... }; 
     (void) dum; 

     // Alternative, sweet and beautiful C++17 fold expression 
     // (void)(bases->process(), ...); 
    } 
} 
+0

Warum die extra 0 im Array ganz vorne? – Aconcagua

+0

@Aconcagua sollte es in diesem Fall nicht sinnvoll sein, fair zu sein.Es ist da, um den Leerpackungsfall richtig zu handhaben, wobei ein Array von null Länge vermieden wird, das schlecht geformt wäre. – Quentin

+0

Können Sie erklären, was mit 'dum []' und der 0 und dem, -Operator 'passiert? –

0

Mit variadische Vorlagen Sie einige kurze von der Kompilierung materialisieren muss Rekursion:

template<unsigned int COL> 
void func(const std::string &fn, Base &v) { 
    ... 
} 

template<unsigned int COL, typename... Args> 
void func(const std::string &fn, Base &v, Args&&... args) { 
    ... 
    func<COL>(fn, std::forward<Args>(args)...); 
} 
1

Verwenden Sie den Packexpansionsoperator ..., um Ihre Variadic-Argumente zu entpacken.

template<typename... T> void nop(T&&...) { } 

template<typename... Bases> 
void func(const std::string &fn, Bases&&... bases) 
{ 
    io::CSVReader<sizeof...(Bases) + 1> in(fn); 

    in.read_header(io::ignore_extra_column, "epoch", bases->column...); 
    double ign; 
    while (in.read_row(ign, bases->value...)) { 
    // multiple ways to call process on all values 
    // prettier with C++17 stuff it seems 
    nop((bases->process(), 0)...); 
    } 
} 
1

zwei Schritten: Zunächst müssen wir unsere Funktion beliebig erweitern:

template <typename ... Bases> 
void f(std::string const& s, Bases* ... values) 
{ 
    io::CSVReader<sizeof...(Bases) + 1> in(s); 
    in.read_header(io::ignore_extra_column, "epoch", values->column ...); 
    double ign; 
    while(in.read_row(ign, values->value ...)) 
    { 
     /* see below! */ process(values...); 
    } 
} 

Bisher kein Problem, read_header und read_row sind variadische Vorlagen, so gut. Das Aufrufen der Member-Funktion war ein wenig schwierig - werfen Sie einen Blick auf den Aufruf der oben genannten (noch unbekannten) Prozessfunktion. Stichwort Kompilierung Rekursion (101010 Antwort), hier gehen wir:

void process() 
{ } 

template <typename ... Bases> 
void process(Base* b, Bases* ... values) 
{ 
    b->process(); 
    process(values ...); 
} 

definieren diese beiden Funktionen vor der Template-Funktion, und es funktioniert ...

Edit: Stehlen sizeof ... (Basen) + 1 von J.Doe ...

0

übersetzbar Beispiel (Sie werden im Code ausfüllen müssen aus der cSV-Datei zum Lesen und zu jedem Ziel zu schreiben):

#include <string> 
#include <cstdint> 
#include <utility> 
#include <tuple> 


template<class Function, class...Ts> 
void for_all(Function f, Ts&&...ts) 
{ 
    using expand = int[]; 
    void(expand{0, 
       (f(std::forward<Ts>(ts)), 0)... 
      }); 

} 

// some mocked io library 
namespace io 
{ 

    template<std::size_t MaxRows> 
    struct CSVReader 
    { 
    CSVReader(const std::string& s) 
    { 
    } 


    template<class...Targets> 
     void read_headers(std::tuple<Targets...>& target) 
    { 
     read_headers_impl(std::make_index_sequence<sizeof...(Targets)>(), target); 
    } 

    template<class...Targets> 
     void read_row(std::tuple<Targets...>& targets) 
    { 
     read_values_impl(std::make_index_sequence<sizeof...(Targets)>(), targets); 
    } 

    // support for std::tie 
    template<class...Targets> 
     void read_row(const std::tuple<Targets...>& targets) 
    { 
     read_values_impl(std::make_index_sequence<sizeof...(Targets)>(), targets); 
    } 

    private: 
    template<std::size_t...Is, class Tuple> 
     void read_headers_impl(std::index_sequence<Is...>, Tuple& target) 
    { 
     for_all([](auto&& target) { 
     // read the header and assign it to target here 
     }, std::get<Is>(target)...); 
    } 

    template<std::size_t...Is, class Tuple> 
     void read_values_impl(std::index_sequence<Is...>, Tuple& target) 
    { 
     for_all([](auto&& target) { 
     // read the values and assign it to target here 
     }, std::get<Is>(target)...); 
    } 

    }; 

} 

struct Base 
{ 
    std::string& value(); 
    void process(); 
}; 

template<std::size_t N, class T, class Current = std::tuple<>> struct n_tuple; 
template<std::size_t N, class T> using n_tuple_t = typename n_tuple<N, T>::type; 

template<std::size_t N, class T, class Current> 
struct n_tuple 
{ 
    using type = std::conditional_t< 
    N == std::tuple_size<Current>::value, 
    Current, 
    decltype(std::tuple_cat(std::declval<Current>(), std::declval<n_tuple_t<N-1, T>>())) 
    >; 
}; 

template<class...Bases> 
void col_n(const std::string &fn, Bases&...bases) 
{ 
    constexpr std::size_t column_count = sizeof...(Bases) + 1; 
    io::CSVReader<column_count> in(fn); 
    using headers_type = n_tuple_t<column_count, std::string>; 
    auto headers = headers_type(); 

    in.read_headers(headers); 
    double ign; 
    auto value_refs = std::tie(ign, bases.value()...); 

    while (in.read_row(value_refs)) { 
    // now we only want to process each base 
    for_all([](auto&& base) { 
     base.process(); 
    }, bases...); 
    } 
} 
Verwandte Themen