2017-10-18 5 views
0

Ich richte einen Konsolenbefehl ein, der eine variable Anzahl von Argumenten akzeptiert, von denen jeder Basistypen (int, float, bool, string) sein kann und sie dann an eine Funktion mit 8 Überladungen zur Unterstützung einer anderen Zahl weitergibt von Argumenten unterschiedlicher Art. Wie würde ich die Befehlszeile in Werte basierend auf ihrem Typ analysieren und dann an die Funktion übergeben?Wie übergibt man eine Variablennummer und einen Typ von Argumenten an die Template-Funktion?

Ich kann jedes Argument über die Funktion const char* GetArg(int index) abrufen. Konvertieren der char* in den richtigen Typ ist kein Problem, also mach dir keine Sorgen über diesen Teil. Das Speichern der Werte und die Weitergabe an die Template-Funktion ist der Teil, auf dem ich feststecke.

So wurde zum Beispiel, wenn der Befehl mit der folgenden Zeichenfolge ausgeführt: „Befehl 66 wahr 5‚String-Wert‘0,56“

Es würde dann in die folgenden args aufgebrochen werden und gespeichert irgendwie:

int arg1 = GetArg(1); // 66 
bool arg2 = GetArg(2); // true 
int arg3 = GetArg(3); // 5 
char* arg4 = GetArg(4); // "string value" 
float arg5 = GetArg(5); // 0.56 

Und dann basierend auf der Anzahl von Argumenten, rufen Sie die Funktion richtige Vorlage:

// The function definition looks something like this: 
void SomeFunc(); 
template<typename T1> 
void SomeFunc(const T1& arg1); 
template<typename T1, typename T2> 
void SomeFunc(const T1& arg1, const T2& arg2); 
// etc... 

// And then somehow it would be called. This is just an example. I don't 
// know how to call it in a way that would work with variable number and 
// type of args. 
switch (argCount) 
{ 
case 0: 
    SomeFunc(); 
    break; 
case 1: 
    SomeFunc(arg1); 
    break; 
case 2: 
    SomeFunc(arg1, arg2); 
    break; 
case 3: 
    SomeFunc(arg1, arg2, arg3); 
    break; 
case 4: 
    SomeFunc(arg1, arg2, arg3, arg4); 
    break; 
case 5: 
    SomeFunc(arg1, arg2, arg3, arg4, arg5); 
    break; 
} 

Wie würden Sie machen dies möglich? Das Speichern der Argumente auf irgendeine Weise, die an die Template-Funktion übergeben werden kann, so dass sie den Typ jedes Arguments kennt, scheint nicht möglich zu sein, aber ich habe das Gefühl, dass ich einfach nicht an etwas denke.

Ich kann diese Schnittstelle auch nicht ändern. Dies ist eine Drittanbieterfunktion, mit der ich mich gerade beschäftigen muss. Also egal wie es implementiert wird, muss es durch SomeFunc() gehen.

WICHTIG: Ich mache das in Visual Studio 2012, also bin ich eher auf neuere C++ - Funktionen beschränkt. Es kann ein bisschen C++ 11 tun, aber das ist es. Ich versuche, das Projekt auf eine neuere Version zu aktualisieren, aber jetzt muss ich damit umgehen.

+0

Es klingt, als ob Sie versuchen, Kompilierungszeit und Laufzeitlogik zu mischen. Vorlagen sind Kompilierzeit, und Sie müssen die Anzahl der Parameter und deren Typ zur Kompilierzeit kennen. Ist es wirklich etwas anderes, was du erreichen willst? –

+0

Nun, wenn Sie ein Tupel erstellen können, können Sie [this] (https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer) verwenden, um das Tupel zu drehen in einen Funktionsaufruf. – NathanOliver

+0

@TommyAndersen Das klingt genau. Ich habe damit wirklich keinen Weg. Ich versuche nur, einen Helferkonsolenbefehl für leicht auslösende Ereignisse zu machen, die eine variable Anzahl von Argumenten und Typen für jede haben können. Wenn das außer Kontrolle gerät, werde ich wahrscheinlich nur auf die Idee kommen, die so aussieht, als wäre es der Fall. – Shenjoku

Antwort

1
using basic_type = std::variant<int, float, bool, std::string>; 

using flat_arguments = std::vector<basic_type>; 

template<std::size_t...Ns> 
using packed_arguments = std::variant< std::array<basic_type, Ns>... >; 

template<class T, std::size_t...Ns> 
std::array<T, sizeof...(Ns)> pack_one(std::vector<T> n, std::index_sequence<Ns...>) { 
    return {{ std::move(n[Ns])... }}; 
} 

template<class T, std::size_t...Ns> 
std::optional<std::variant< std::array<T, Ns>... >> 
pack_all(std::vector<T> n, std::index_sequence<Ns...>) { 
    std::optional<std::variant< std::array<T, Ns>... >> retval; 
    if (n.size() >= sizeof...(Ns)) { return retval; } 
    (
    (
     (n.size()==Ns)? 
     void(retval.emplace(pack_one(std::move(n), std::make_index_sequence<Ns>{}): 
     void() 
    ),... 
); 
    return retval; 
} 

flat_arguments get_arguments(int argc, char const* const*argv); // write this 

auto invoke_somefunc = [](auto&&...args){ 
    return SomeFunc(decltype(args)(args)...); 
}; 

int main(int argc, char const*const* argv) { 
    auto args = get_arguments(argc, argv); 
    auto args_packed = pack_all(std::move(args), std::make_index_sequence<9>{}); 
    if (!args_packed) return -1; 
    std::visit([](auto&& args){ 
    std::apply([](auto&&...args){ 
     std::visit(invoke_somefunc, args...); 
    }, args); 
    }, args_packed); 
} 

das sollte es tun. Enthält wahrscheinlich Tippfehler. .

boost hat äquivalente Typen (variant und optional), die die Verwendung std oben, mit einigen Verbesserungen ersetzen könnte.

Die Erweiterung des Faltenpacks kann durch den Expand-in-Array-Hack in oder höher ersetzt werden.

+0

Entschuldigung, ich habe vergessen zu erwähnen, dass ich bei neueren Sprachfunktionen ziemlich eingeschränkt bin. Ich mache das in VS2012, also glaube ich nicht, dass das meiste davon verfügbar ist. Ich habe den ursprünglichen Beitrag mit diesen Informationen aktualisiert. – Shenjoku

+0

@Shenjoku Oh, dann nicht, können Sie nicht. Ich meine, da ist die Turing-Teer-Grube, in der man alles machen kann, aber man erweitert die oben genannte Arbeit im Grunde genommen in eine ganze Menge Spaghetti-Code. Es ist viel einfacher, Ihren C++ - Compiler zu aktualisieren, als ihn in die von VS 2012 unterstützte Subsprache C++ zu schreiben. Vielleicht, wenn Sie eine alte Version von Boost finden können, die Variante unterstützt, funktioniert aber in VS 2012 und schreiben Sie manuell die Funktionsobjekte, um die Lambdas und all diesen Schmerz zu ersetzen. – Yakk

Verwandte Themen