2016-09-14 1 views
1

Ich verwende fmtlib und muss eine dynamische Liste von Argumenten erstellen. Grundsätzlich habe ich einen Formatstring wie pro der Dokumentation SeiteErstellen einer dynamischen Liste von benannten Argumenten für Fmtlib

fmt::print("Hello, {name}", fmt::arg("name", "test"), ...); 

aber die Argumente Liste (einschließlich, wie viele Argumente gibt es) nur zur Laufzeit bekannt ist. Ich habe fmt::ArgList betrachtet, die eine Liste von fmt::Arg Instanzen nimmt. Aber die benannten Argumente sind eine interne Klasse fmt::internal::NamedArg, die ich nicht sehen kann, wie man zu der Liste geht.

Irgendwelche Ideen?

+0

Ohne zu tief in den Code einzutauchen dosn't es möglich zu sein scheinen. Benutze entweder String-Streams oder schreibe die [fmt libraries write API] (http://fmtlib.net/3.0.0/#write-api)? Oder wenn die Daten, die Sie drucken möchten, die gleichen sind, dann verwenden Sie vielleicht etwas, wie in [dieses Problem bezüglich 'std :: initializer_list'] (https://github.com/fmtlib/fmt/issues/181) beschrieben, sollte es leicht zu ersetzen sein mit z ein 'std :: vector'. Unter Verwendung der in der Ausgabe dargestellten Lösung könnten Sie z.B. ein Vektor von 'std :: any' (oder' boost :: any'). Wenn Sie keinen einheitlichen Typ für alle Artikel haben, die Sie drucken möchten. –

Antwort

1

Ich fand eine Lösung mit fmtlib Interna. Der folgende Code formatiert mithilfe von fmtlib einen String aus einem String-String-Dictionary. Für arg-counts> = 16 musste eine spezielle Behandlung eingeschlossen werden, da fmtlib eine Optimierung für kleinere Argumentlisten verwendet.

// helper only: 
inline void set_type(fmt::ULongLong& result, uint32_t index, fmt::internal::Arg::Type t) 
{ 
    unsigned shift = index * 4; 
    uint64_t mask = 0xf; 
    result |= static_cast<uint64_t>(t) << shift; 
} 

// input: 
//  pattern = fmt::format string 
//  vars = dictionary of string/string arguments 
// output: 
//  formatted string 
std::string dformat(const std::string& pattern, const std::unordered_map<std::string, std::string>& vars) 
{ 
    // this is a vector of "named arguments" - straightforward enough. 
    std::vector<fmt::internal::NamedArg<char>> av; 

    // fmtlib uses an optimization that stores the types of the first 16 arguments as 
    // bitmask-encoded 64-bit integer. 
    fmt::ULongLong types = 0; 

    // we need to build the named-arguments vector. 
    // we cannot resize it to the required size (even though we know it - we have the 
    // dictionary), because NamedArg has no default constructor. 
    uint32_t index = 0; 
    for (const auto& item : vars) 
    { 
     av.emplace_back(fmt::internal::NamedArg<char>(item.first, item.second)); 

     // we need to pack the first 16 arguments - see above 
     if (index < fmt::ArgList::MAX_PACKED_ARGS) 
     { 
      set_type(types, index, fmt::internal::Arg::NAMED_ARG); 
     } 
     ++index; 
    } 

    // and this is a bit tricky: depending on the number of arguments we use two mutually 
    // incompatible vectors to create an arglist. It has everything to do with the speed 
    // (and memory) optimization above, even though the code looks nearly identical. 
    if (index >= fmt::ArgList::MAX_PACKED_ARGS) 
    { 
     std::vector<fmt::internal::Arg> avdata; 

     // note the additional terminating Arg::NONE 
     avdata.resize(vars.size() + 1); 
     index = 0; 
     for (const auto& item : av) 
     { 
      avdata[index].type = fmt::internal::Arg::NAMED_ARG; 
      avdata[index].pointer = &av[index]; 
      ++index; 
     } 
     return fmt::format(pattern, fmt::ArgList(types, &avdata[0])); 
    } 
    else 
    { 
     std::vector<fmt::internal::Value> avdata; 

     // no need for terminating Arg::NONE, because ARG_NONE is the last encoded type 
     avdata.resize(vars.size()); 
     index = 0; 
     for (const auto& item : av) 
     { 
      avdata[index].pointer = &av[index]; 
      ++index; 
     } 
     return fmt::format(pattern, fmt::ArgList(types, &avdata[0])); 
    } 
} 

Beispiel Nutzung:

std::unordered_map<std::string, std::string> vars; 
vars["FIRSTNAME"] = "Foo"; 
vars["LASTNAME"] = "Bar"; 

std::string result = dformat("Hello {FIRSTNAME} {LASTNAME}, how are you doing", vars); 
Verwandte Themen