2010-05-13 5 views
5

Ich frage mich, ob in C/C++ Sprache ist es möglich, Argumente zu übergeben, um in Schlüssel-Wert-Form zu funktionieren. Zum Beispiel in Python können Sie tun:Variadische Funktionen und Argumente Zuweisung in C/C++

def some_function(arg0 = "default_value", arg1): 
    # (...) 

value1 = "passed_value" 
some_function(arg1 = value1) 

So ist der alternative Code in C könnte wie folgt aussehen:

void some_function(char *arg0 = "default_value", char *arg1) 
{ 
    ; 
} 

int main() 
{ 
    char *value1 = "passed_value"; 
    some_function(arg1 = value1); 
    return(0); 
} 

die Argumente also in some_function zu verwenden wäre:

arg0 = "default_value"

arg1 = "bestandener_wert"

Irgendwelche Ideen?

Antwort

8

Hier ist ein C99-Lösung mit Verbindung Literale und variadische Makros:

#include <stdio.h> 

#define some_func(...) some_func_((struct some_func_args_){ __VA_ARGS__ }) 

struct some_func_args_ 
{ 
    const char *arg1; 
    const char *arg2; 
}; 

static void some_func_(struct some_func_args_ args) 
{ 
    if(!args.arg1) args.arg1 = "default"; 
    printf("---\narg1 = %s\narg2 = %s\n", args.arg1, args.arg2); 
} 

int main(void) 
{ 
    some_func("foo", "bar"); 
    some_func(.arg1 = "spam"); 
    some_func(.arg2 = "eggs"); 
    return 0; 
} 
+0

Ich mag das. Sehr kreativ und sauber. – nategoose

+3

Beachten Sie jedoch, dass es mit dieser Methode unmöglich ist, ein Null/NULL-Argument von einem ausgelassenen Argument zu unterscheiden. – caf

1

Nein, Parameter in C oder C++ können nicht namentlich übergeben werden.

Beide Sprachen unterstützen Standardargumente für die Parameter hinter in einer Funktion. Das heißt, jedes Argument für eine Funktion kann einen Standardwert haben, aber alle nachfolgenden Argumente, nachdem der erste Standardwert gefunden wurde, müssen auch Standardwerte haben.

Zum Beispiel sind die folgenden alle gültig:

void f(int a, int b = 0); 
void g(double a = 1, double b = 2); 
void h(int a = 3, int b = 2, int c = 1, int d = 0); 
void i(float a, float b, float c = 1, float d = 2); 

Aber die keine der folgenden gelten:

void j(int a = 1, int b); 
void k(int a, int b = 1, int c); 
void l(int a = 2, int b = 1, int c); 

Der Grund ist einfach: Da Sie keine Parameter mit Namen in C passieren kann und C++, der einzige Weg, um zu wissen, welche Parameter die Standardwerte verwenden sollen und welche die übergebenen Werte verwenden sollen, besteht darin, die übergebenen Werte den Parametern in der richtigen Reihenfolge zuzuordnen und dann die Standardwerte (in der Reihenfolge) für alles zu verwenden.

So werden die folgenden Anrufe gelten unter Verwendung der oben genannten Funktionen:

f(0); // calls f(0, 1); 
g(); // calls g(1,2); 
g(10); // calls g(10,2); 
h(); // calls h(3,2,1,0); 
h(1,2); // calls h(1,2,1,0); 
+4

"Beide Sprachen unterstützen Standardargumente zu den abschließenden Parametern in einer Funktion."-> Nein, C hat keine Standardargumente. –

+0

C erlaubt * nicht * Standardparameter. –

+0

-1, siehe Litbs Vorschlag –

6

Sie dies in C++ emulieren kann mit diesem:

struct params { 
    string foo_; 
    double bar_; 
    short xxx_; 
    params() : foo_("123"), bar_(3.1415), xxx_(42) {} // default parameters 
    params& foo(string s) {foo_=s;return *this;} 
    params& bar(double x) {bar_=x;return *this;} 
    params& xxx(short x) {xxx_=x;return *this;} 
}; 

void some_function(params const & p); 

int main() { 
    some_function(params().bar(99.9).xxx(23)); 
} 

Aber IMHO ist es nicht der Mühe wert. Zu viel Kesselplatte.

Wenn ich mich richtig erinnere, enthält Stroustrups Buch "Design and Evolution of C++" einen Abschnitt, der diese Feature-Anfrage von "benannte Argumente" behandelt. Die Schlussfolgerung war etwas im Sinne von: keine gute Idee. Überprüfen Sie es, wenn Sie Details wünschen.

+0

Dieser Ansatz der Verwendung einer params-Struktur wird in C++ als" benannte Parametersprache "bezeichnet. –

6

Benannte Argumente werden nicht in C unterstützt, aber Sie Parameter es variadische Funktion namens emulieren könnte (obwohl Sie Sicherheit lose Art):

#include <stdarg.h> 

void do_sth (int foo, ...) 
{ 
    int baz = 7;    /* "baz" argument */ 
    const char *xyz = "xyz"; /* "xyz" argument */ 

    /* Parse named parameters */ 
    va_list ap; 
    va_start (ap, foo); 
    for (;;) { 
     const char *key = va_arg (ap, char *); 
     if (key == NULL) { 
      /* Terminator */ 
      break; 
     } else if (strcmp (key, "baz") == 0) { 
      baz = va_arg (ap, int); 
     } else if (strcmp (key, "xyz") == 0) { 
      xyz = va_arg (ap, char *); 
     } else { 
      /* Handle error */ 
     } 
    } 
    va_end (ap); 

    /* do something useful */ 
} 

do_sth (1, NULL);        // no named parameters 
do_sth (2, "baz", 12, NULL);     // baz = 12 
do_sth (3, "xyz", "foobaz", NULL);   // xyz = "foobaz" 
do_sth (4, "baz", 12, "xyz", "foobaz", NULL); // baz = 12, xyz = "foobaz" 

nur optionale Argumente Liste mit NULL enden erinnern. Sie könnten auch einige ENUM als Schlüssel anstelle von Zeichenketten verwenden.

Diese Technik verwendet wird, für exapmle, in GTK +:

+0

Wer gab eine absolut gültige Antwort ohne ersichtlichen Grund ab? +1 von mir. – qrdl

-1

C++ einzige Lösung: Sie boost könnte :: any, std :: map und std :: string, um ein Objekt mit den möglichen Argumenten zu erstellen.

#include <iostream> 
#include <map> 
#include <string> 

#include "boost/any.hpp" 

using namespace std; 

void variableArguments(map<string, boost::any> arguments){ 
    cout << ">>> variableArguments begins" << endl; 

    if(arguments.find("intParameter") != arguments.end()){ 
    cout << "Found int parameter: " 
     << boost::any_cast<int>(arguments["intParameter"]) << endl; 
    } 

    if(arguments.find("stringParameter") != arguments.end()){ 
    cout << "Found string parameter: " 
     << boost::any_cast<string>(arguments["stringParameter"]) << endl; 
    } 

    cout << "<<< variableArguments ends" << endl; 

} 

int main(int argc, char *argv[]) 
{ 

    map<string, boost::any> argMap; 
    argMap["intParameter"] = 5; 
    argMap["stringParameter"] = string("MyString"); 

    variableArguments(argMap); 

    argMap.erase(argMap.find("intParameter")); 

    variableArguments(argMap); 

    return 0; 
} 
+0

Warum eine Abhängigkeit von boost :: any hinzufügen, eine Compiler-Optimierung und Performance-Firewall sowie hardcodierte Strings einführen, die potentielle Debugging-Albträume ergeben, wenn eine Struktur eingeführt wird ist so einfach: 'struct SimpleParams [int foo; Zeichenfolgeleiste; bool frob;};' –

+0

Und natürlich: Warum erzwinge eine nichtkonstante, statusbehaftete Variable auf den Aufrufer? –

+0

Sicher, weil eine neue Struktur für jede andere Menge erstellt der Argumente, die Sie übergeben möchten, ist so brillant –

3

Dies ist nicht verfügbar in Vanille C oder C++. Es gibt jedoch eine C++ Boost-Bibliothek, mit der Sie dies für Funktionen ausführen können, die Sie schreiben: Boost.Parameter. Zu leihen eines ihrer Beispiele, ist seine Verwendung Art wie folgt aus:

myclass x("bob", 3);      // positional 
myclass y(_index = 12, _name = "sally"); // named 
myclass z("june");      // positional/defaulted 

Umsetzung dieses für Ihre Funktionen sieht etwas beteiligt, jedoch. Sie können entscheiden, dass es sich nicht lohnt.

0
#define call_foo(...) \ 
    do { \ 
     int first; \ 
     int second; \ 
     int third; \ 
     (__VA_ARGS__); \ 
     foo(first, second, third); \ 
    } while (0) 

.... 

int main(void) { 
    call_foo(first=9, third=5, second=3); 
} 

Erste Rückgabewerte in einem nicht klobig Weise schwieriger ist.

Dies würde ermöglichen es Ihnen, sehr dumme Dinge zu tun (was ich hasse) wie Standardwerte für Ihre Argumente angeben:

#define call_foo(...) \ 
    do { \ 
     int first = default_first; \ 
     int second; \ 
     int third; \ 
     (__VA_ARGS__); \ 
     foo(first, second, third); \ 
    } while (0) 

Dies wird fehlschlagen, wenn Sie tun:

int first = 9; 
    call_foo(first=first, third=5, second=3); 

weil in Die Makroerweiterung first=first bedeutet, dass die lokale first mit sich selbst initialisiert wird. Ich habe darüber nachgedacht, dies mit der Preprozessor-Verkettung zu umgehen, aber das wird komplizierter. Sie müssten die gleiche Anzahl von Makroparametern wie Funktionsparameter auflisten.

#define call_foo(x, y , z) \ 
    do { \ 
     int call_foo_first; \ 
     int call_foo_second; \ 
     int call_foo_third; \ 
     call_foo_##x; \ 
     call_foo_##y; \ 
     call_foo_##z; \ 
     foo(call_foo_first, call_foo_second, call_foo_third); \ 
    } while (0) 

Die call_foo_##x; Linie würde in call_foo_first=first; wenden, wenn im vorherigen Beispiel genannt, so dass jedes Symbol hat seinen eigenen einzigartigen Namen. Dies macht den Standard-Trick schwieriger, weil Sie etwas angeben müssen, um diesen Punkt in der Makroargumentliste zu füllen.

Wenn das Makro mit einem Standardargument für first definiert wurden dann:

call_foo(first, third=7, second=8); 

Dies zwingt Sie alle Parameter zur Liste (oder zumindest etwas, das in Rechts C führt - Sie zweimal zweite aufgeführt haben könnte, aber du hättest das trotzdem machen können) um dieses Makro zu benutzen.

Ich denke, Sie sollten in der Lage sein, die letzte Version dieses für Variable Argument gelisteten Makros/Funktionen zu erweitern, aber die optionalen Parameter müssten immer noch am Ende der Liste übergeben werden und Sie müssten alle davon verbrauchen die obligatorischen Stellen, bevor Sie zu den optionalen Argumenten kamen.

Verwandte Themen