2017-12-15 7 views
2

ich auf folgendes Problem gestolpert, während ein ‚allgemeinen‘ Leser beachten:Operator >> auf einen vertraglich gebundenen Tupel mit std :: ignoriert

Der folgende Code funktioniert völlig in Ordnung (Sie müssen C++ 1z Unterstützung kompilieren wie es constexpr wenn, aber mit geringfügigen Modifikationen verwendet, sollte es auch mit C++ 11) kompilieren:

#include <vector> 
#include <string> 
#include <type_traits> 
#include <tuple> 
#include <sstream> 

using namespace std; 

template<int N, class tuple_type> 
struct fill_tuple { 
    static void write(std::vector<std::string>& container, tuple_type& tuple) 
    { 
     // use operator >> to fill the N-1'th member of the tuple 
     std::stringstream(container[N - 1]) >> std::get<N - 1>(tuple); 
     if constexpr(N > 1){ // Continue if there are till fields to read 
      fill_tuple<N - 1, tuple_type>::write(container, tuple); 
     } 
    } 
}; 

template<class tuple_type> 
void read (std::vector<std::string>& container, tuple_type obj){ 
    fill_tuple<std::tuple_size<tuple_type>::value, tuple_type>::write(container, obj); 
} 

struct some_data { 
    char a; 
    char b; 
    char c; 
    char d; 

    auto content() { 
     return std::tie(a,b,c,d); 
    } 
}; 

int main() 
{ 
    std::vector<std::string> some_strings = {"a","b","c","d"}; 
    // Read some_strings into some_data 
    some_data foo; 
    read(some_strings, foo.content()); 
} 

aus Gründen der Einfachheit halber alle gebundenen Kontrollen (wie tuple_size < = Behältergröße) entfallen.

Wenn ich eine Struktur analysieren wollte, die nur Mitglieder a, b und d unter Verwendung eines Containers mit einer Größe von 4 hat, war meine Intuition einfach std::tie(a,b,c,d) zu std::tie(a,b,std::ignore,d) umzuschreiben.

Dies ist jedoch fehlgeschlagen, da std::ignore (oder GCC-Implementierung) scheint keine operator>> Funktion zu haben. Ich habe bereits versucht, gegen std::ignore mit std::is_same: std::is_same<typename std::remove_reference<typename std::tuple_element<N - 1,tuple_type>::type>::type, std::ignore>::value zu überprüfen, aber das schlägt auch fehl.

Meine Frage ist: Gibt es eine Möglichkeit, gegen std :: ignore zu überprüfen, oder sogar besser, ersetzen Sie es vollständig ohne vorherige Änderungen an den Container-Vektor angewiesen?

+2

'std :: ignore' ist "Objekt-like", nicht "Typ-like". Versuchen Sie, "declltype" darum zu legen. – Incomputable

+0

"Der folgende Code funktioniert einwandfrei" - wirklich, auch ohne ";" nach der struct deklaration? –

+0

Sorry @ EdgarRokyan, ich habe den Code bearbeitet, um tatsächlich zu kompilieren. –

Antwort

2

ersetzen Sie es vollständig ohne vorherige Modifikationen am Container-Vektor angewiesen?

statt is_same zu verwenden, können Sie nur Überlastung gegen decltype(ignore); in C++ 17:

template<typename T> 
void read_element(std::string const& s, T& t) { std::stringstream{s} >> t; } 
void read_element(std::string const&, decltype(std::ignore) const&) { /*do nothing*/ } 

std::apply([&](auto&... args) 
     { 
      auto it = some_vector_of_strings.begin(); 
      (read_element(*it++, args), ...); 
     }, tuple); 

die gleiche Idee gilt auch für Ihren C++ 11-Code.

+0

Tag basierte Versand funktioniert immer noch, lol. Obwohl es umständlicher ist, damit umzugehen. – Incomputable

+0

@incomputable yep, Tag-Dispatching kann noch geeigneter sein, wenn komplexere read_element Logik erforderlich ist. Das heißt, ich glaube, die ursprüngliche Absicht des OP war es, * wenn constexpr * hier zu verwenden. –

+0

Ich schäme mich etwas dafür, dass ich selbst nicht dazu gekommen bin. Wie auch immer, ich mag Ihre Antwort sehr, aber für die Aufzeichnung muss es in meinem speziellen Fall "declltype (std :: ignore)" & sein. –

1

Als Zustand in Kommentar, std::ignore ist kein Typ, sondern ein Objekt, müssen Sie decltype(std::ignore) verwenden, um den Typ zu erhalten.

template <typename T> 
void read_simple(const std::string& s, T& obj) 
{ 
    std::stringstream(s) >> obj; 
} 

void read_simple(const std::string&, const decltype(std::ignore)&) {} 

template <std::size_t ... Is, typename Tuple> 
void read(const std::vector<std::string>& container, 
      Tuple&& obj, 
      std::index_sequence<Is...>) 
{ 
    (read_simple(container[Is], std::get<Is>(obj)), ...); 
} 

template <typename Tuple> 
void read(const std::vector<std::string>& container, Tuple&& obj) 
{ 
    read(container, 
     obj, 
     std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()); 
} 

Demo

+0

für den Datensatz, AFAIK der Typ der Ignorieren möglicherweise nicht kopierbar, so sollte es durch Verweis für die Portabilität Willen übergeben werden ... –

+0

@MassimilianoJanes: behoben. – Jarod42

Verwandte Themen