2010-05-13 12 views
5

Me preguntaba si en lenguaje C/C++ es posible pasar argumentos para que funcionen en forma de clave-valor. Por ejemplo en Python que puede hacer:Asignación de variables y funciones en C/C++

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

value1 = "passed_value" 
some_function(arg1 = value1) 

Así que el código alternativo en C podría tener este aspecto:

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

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

Así que los argumentos a utilizar en some_function sería:

arg0 = "default_value"

arg1 = "passed_value"

¿Alguna idea?

Respuesta

8

he aquí una solución C99 usando literales compuestos y macros variadic:

#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

Me gusta esto. Muy creativo y limpio. – nategoose

+3

Tenga en cuenta que con este método es imposible diferenciar un argumento cero/NULO de un argumento omitido. – caf

1

No, no puede pasar los parámetros por nombre en C o C++.

Ambos lenguajes admiten argumentos predeterminados para los parámetros posteriores en una función. Es decir, cualquier argumento para una función puede tener un valor predeterminado, pero todos los argumentos posteriores después de que se encuentre el primer valor predeterminado también deben tener valores predeterminados.

Por ejemplo, los siguientes son todas válidas:

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); 

Pero el que ninguno de los siguientes son válidos:

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); 

La razón es simple: Puesto que no puede pasar parámetros por su nombre en C y C++, la única forma de saber qué parámetros usar los valores predeterminados y cuál usar los valores pasados ​​es mediante la asignación de los valores pasados ​​a los parámetros en orden y luego usando los valores predeterminados (en orden) para cualquier cosa sobrante.

Así, las llamadas siguientes son válidos utilizando las funciones anteriores:

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

"Ambos lenguajes admiten argumentos predeterminados para los parámetros finales en una función."-> No, C no tiene argumentos predeterminados. –

+0

C no permite * los parámetros por omisión –

+0

-1, consulte la sugerencia de litb –

6

Puede emular esto en C++ con esto:

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)); 
} 

Pero en mi humilde opinión no vale la pena el esfuerzo. Demasiada placa de la caldera.

Si no recuerdo mal el libro de Stroustrup "Diseño y Evolución de C++" contiene una sección que analiza esta solicitud de función de "argumentos con nombre". La conclusión fue algo así como: no es una buena idea. Compruébalo si quieres detalles.

+0

Este enfoque de utilizar una estructura params se conoce como el" idioma de parámetro nombrado "en C++. –

6

argumentos con nombre no están soportados en C, pero que podrían emular a los parámetros que utilizan la función variadic llamado (aunque la seguridad de tipos sueltos):

#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" 

Sólo recuerde para poner fin a la lista de argumentos opcionales con NULL. También podría usar algunos ENUM como teclas en lugar de cadenas de caracteres.

Esta técnica se utiliza, por exapmle, en GTK +:

+0

Quién rechazó la respuesta absolutamente válida sin motivo aparente +1 de mí – qrdl

-1

C++ única solución: usted podría utilizar impulso :: ninguna, std :: map y std :: string para crear un objeto con los posibles argumentos.

#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

Por qué agregar una dependencia a boost :: any, introducir un firewall de optimización y compilación del compilador, así como cadenas codificadas, produciendo posibles pesadillas de depuración, cuando se introduce una estructura es tan simple: 'struct SimpleParams [int foo; string bar; bool frob;};' –

+0

Y por supuesto: ¿Por qué imponer una variable no const, stateful a la persona que llama? –

+0

Claro, porque crear una nueva estructura para cada conjunto diferente de los argumentos que desea pasar es tan brillante. –

3

Esto no está disponible en vainilla C o C++. Sin embargo, hay una biblioteca C++ Boost que le permite hacer esto para las funciones que escribe: Boost.Parameter. Para tomar prestado uno de sus ejemplos, su uso es algo así como esto:

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

La implementación de este para sus funciones tiene un aspecto un tanto complicado, sin embargo. Puede decidir que no vale la pena el esfuerzo.

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); 
} 

Obtención de valores de retorno de una manera torpe no es más difícil.

Esto permitirá hacer cosas muy tontas (que odio), tales como especificar valores predeterminados para sus argumentos:

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

Esto se producirá un error si lo hace:

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

porque en la macro expansión first=first significará inicializar el first local consigo mismo. Pensé en tratar de evitar esto con la concatenación del preprocesador, pero eso se vuelve más complicado. Debería enumerar el mismo número de parámetros de macro como parámetros de función.

#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) 

La línea call_foo_##x; se convertiría en call_foo_first=first; si es llamado en el ejemplo anterior, por lo que cada símbolo tiene su propio nombre único. Esto hace que el truco del argumento predeterminado sea más incómodo porque tendría que especificar algo para completar ese punto en la lista de argumentos de macros.

Si la macro se define con un argumento predeterminado para first a continuación:

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

Esto obliga a enumerar todos los parámetros (o al menos algo que se traduce en C legal - que podría haber enumerado dos veces segundo, pero podrías haberlo hecho de todos modos) para usar esta macro.

Creo que debería poder extender la última versión de esto para las macros/funciones enumeradas de argumento variable, pero los parámetros opcionales aún tendrían que pasarse al final de la lista y tendría que usar todos los los puntos obligatorios antes de llegar a los argumentos opcionales.

Cuestiones relacionadas