C arguments par défaut

Existe-t-il un moyen de spécifier des arguments par défaut pour une fonction dans C?

    Pas vraiment. Le seul moyen serait d’ écrire une fonction varargs et de renseigner manuellement les valeurs par défaut des arguments que l’appelant ne transmet pas.

    Wow, tout le monde est tellement pessimiste ici. La réponse est oui.

    Ce n’est pas sortingvial: à la fin, nous aurons la fonction de base, une structure de support, une fonction wrapper et une macro autour de la fonction wrapper. Dans mon travail, j’ai un ensemble de macros pour automatiser tout cela; Une fois que vous aurez compris le stream, il vous sera facile de faire la même chose.

    J’ai écrit ceci ailleurs, alors voici un lien externe détaillé pour compléter le résumé ici: http://modelingwithdata.org/arch/00000022.htm

    Nous aimerions tourner

     double f(int i, double x) 

    dans une fonction qui prend par défaut (i = 8, x = 3,14). Définir une structure compagnon:

     typedef struct { int i; double x; } f_args; 

    Renommez votre fonction f_base et définissez une fonction wrapper qui définit les parameters par défaut et appelle la base:

     double var_f(f_args in){ int i_out = in.i ? in.i : 8; double x_out = in.x ? in.x : 3.14; return f_base(i_out, x_out); } 

    Ajoutez maintenant une macro en utilisant les macros variadic de C. De cette façon, les utilisateurs n’ont pas besoin de savoir qu’ils f_args une structure f_args et pensent qu’ils font les f_args normalement:

     #define f(...) var_f((f_args){__VA_ARGS__}); 

    OK, maintenant tous les éléments suivants fonctionneraient:

     f(3, 8); //i=3, x=8 f(.i=1, 2.3); //i=1, x=2.3 f(2); //i=2, x=3.14 f(.x=9.2); //i=8, x=9.2 

    Vérifiez les règles sur la manière dont les initialiseurs composés définissent les valeurs par défaut pour les règles exactes.

    Une chose qui ne fonctionnera pas: f(0) , car nous ne pouvons pas distinguer entre une valeur manquante et zéro. D’après mon expérience, c’est une chose à surveiller, mais qui peut être prise en charge au besoin – la moitié du temps, votre valeur par défaut est zéro.

    J’ai pris la peine d’écrire ceci parce que je pense que les arguments et les valeurs par défaut nommés facilitent vraiment le codage dans C et le rendent encore plus amusant. Et C est génial d’être si simple et d’avoir encore assez pour rendre tout cela possible.

    Oui. 🙂 Mais pas comme on pourrait s’y attendre.

     int f1(int arg1, double arg2, char* name, char *opt); int f2(int arg1, double arg2, char* name) { return f1(arg1, arg2, name, "Some option"); } 

    Malheureusement, C ne vous permet pas de surcharger les méthodes pour vous retrouver avec deux fonctions différentes. Cependant, en appelant f2, vous appelez réellement f1 avec une valeur par défaut. Ceci est une solution «Ne pas répéter vous-même», qui vous aide à éviter de copier / coller du code existant.

    Nous pouvons créer des fonctions qui utilisent des parameters nommés (uniquement) pour les valeurs par défaut. Ceci est la suite de la réponse de bk.

     #include  struct range { int from; int to; int step; }; #define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__}) /* use parentheses to avoid macro subst */ void (range)(struct range r) { for (int i = r.from; i <= r.to; i += r.step) printf("%d ", i); puts(""); } int main() { range(); range(.from=2, .to=4); range(.step=2); } 

    La norme C99 définit que les noms ultérieurs dans l'initialisation remplacent les éléments précédents. Nous pouvons également avoir certains parameters de position standard, il suffit de changer la signature de la macro et de la fonction en conséquence. Les parameters de valeur par défaut ne peuvent être utilisés que dans un style de paramètre nommé.

    Sortie du programme:

     1 2 3 4 5 6 7 8 9 10 2 3 4 1 3 5 7 9 

    OpenCV utilise quelque chose comme ceci :

     /* in the header file */ #ifdef __cplusplus /* in case the comstackr is a C++ comstackr */ #define DEFAULT_VALUE(value) = value #else /* otherwise, C comstackr, do nothing */ #define DEFAULT_VALUE(value) #endif void window_set_size(unsigned int width DEFAULT_VALUE(640), unsigned int height DEFAULT_VALUE(400)); 

    Si l’utilisateur ne sait pas ce qu’il doit écrire, cette astuce peut être utile:

    exemple d'utilisation

    Non.

    Même la toute dernière norme C99 ne le permet pas.

    Non, c’est une fonctionnalité de langage C ++.

    Réponse courte: non

    Réponse légèrement plus longue: Il existe une ancienne solution de contournement où vous transmettez une chaîne que vous parsingz pour les arguments facultatifs:

     int f(int arg1, double arg2, char* name, char *opt); 

    où opt peut inclure la paire “nom = valeur” ou quelque chose, et que vous appelez comme

     n = f(2,3.0,"foo","plot=yes save=no"); 

    Evidemment, ceci n’est qu’occasionnellement utile. Généralement, lorsque vous souhaitez une interface unique pour une famille de fonctionnalités.


    Vous trouvez toujours cette approche dans des codes de physique des particules écrits par des programmes professionnels en c ++ (comme par exemple ROOT ). Son principal avantage est qu’il peut être prolongé presque indéfiniment tout en maintenant la compatibilité.

    Une autre option utilise struct s:

     struct func_opts { int arg1; char * arg2; int arg3; }; void func(int arg, struct func_opts *opts) { int arg1 = 0, arg3 = 0; char *arg2 = "Default"; if(opts) { if(opts->arg1) arg1 = opts->arg1; if(opts->arg2) arg2 = opts->arg2; if(opts->arg3) arg3 = opts->arg3; } // do stuff } // call with defaults func(3, NULL); // also call with defaults struct func_opts opts = {0}; func(3, &opts); // set some arguments opts.arg3 = 3; opts.arg2 = "Yes"; func(3, &opts); 

    Le meilleur moyen de le faire (ce qui peut ou peut ne pas être possible dans votre cas en fonction de votre situation) est de passer en C ++ et de l’utiliser comme un «meilleur». Vous pouvez utiliser C ++ sans utiliser de classes, de modèles, de surcharge d’opérateur ou d’autres fonctionnalités avancées.

    Cela vous donnera une variante de C avec une surcharge de fonctions et des parameters par défaut (et quelles que soient les autres fonctionnalités que vous avez choisi d’utiliser). Vous devez juste être un peu discipliné si vous voulez vraiment utiliser uniquement un sous-ensemble restreint de C ++.

    Beaucoup de gens diront que c’est une idée terrible d’utiliser C ++ de cette façon, et ils pourraient avoir un point. Mais c’est juste une opinion; Je pense qu’il est valable d’utiliser les fonctionnalités de C ++ avec lesquelles vous êtes à l’aise sans avoir à acheter le tout. Je pense qu’une grande partie de la raison du succès de C ++ est qu’il a été utilisé par beaucoup de programmeurs au début de cette période.

    Non.

    Oui, avec les fonctionnalités de C99, vous pouvez le faire. Cela fonctionne sans définir de nouvelles structures de données ou à peu près et sans que la fonction ait à décider au moment de l’exécution comment elle a été appelée, et sans surcharge de calcul.

    Pour une explication détaillée voir mon post à

    http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/

    Jens

    Non, mais vous pouvez envisager d’utiliser un ensemble de fonctions (ou de macros) pour s’approcher des arguments par défaut:

     // No default args int foo3(int a, int b, int c) { return ...; } // Default 3rd arg int foo2(int a, int b) { return foo3(a, b, 0); // default c } // Default 2nd and 3rd args int foo1(int a) { return foo3(a, 1, 0); // default b and c } 

    Généralement non, mais dans gcc Vous pouvez rendre le dernier paramètre de funcA () facultatif avec une macro.

    Dans funcB (), j’utilise une valeur spéciale (-1) pour signaler que j’ai besoin de la valeur par défaut pour le paramètre ‘b’.

     #include  int funcA( int a, int b, ... ){ return a+b; } #define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 ) int funcB( int a, int b ){ if( b == -1 ) b = 8; return a+b; } int main(void){ printf("funcA(1,2): %i\n", funcA(1,2) ); printf("funcA(1): %i\n", funcA(1) ); printf("funcB(1, 2): %i\n", funcB(1, 2) ); printf("funcB(1,-1): %i\n", funcB(1,-1) ); } 

    J’ai amélioré la réponse de Jens Gustedt pour que:

    1. les fonctions en ligne ne sont pas utilisées
    2. les valeurs par défaut sont calculées lors du prétraitement
    3. macros modulaires réutilisables
    4. possible de définir l’erreur du compilateur qui correspond de manière significative à la casse d’arguments insuffisants pour les valeurs par défaut autorisées
    5. les valeurs par défaut ne sont pas obligées de former la queue de la liste de parameters si les types d’argument restront sans ambiguïté
    6. interopit avec C11 _Generic
    7. variez le nom de la fonction par le nombre d’arguments!

    variadic.h:

     #ifndef VARIADIC #define _NARG2(_0, _1, _2, ...) _2 #define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0) #define _NARG3(_0, _1, _2, _3, ...) _3 #define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0) #define _NARG4(_0, _1, _2, _3, _4, ...) _4 #define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0) #define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5 #define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0) #define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6 #define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0) #define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7 #define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) #define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8 #define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9 #define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__) #define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__)) #define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__) #define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__) // Vary function name by number of arguments supplied #define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name () #define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__) #endif 

    Scénario d’utilisation simplifié:

     const uint32* uint32_frombytes(uint32* out, const uint8* in, size_t bytes); /* The output buffer defaults to NULL if not provided. */ #include "variadic.h" #define uint32_frombytes_2( b, c) NULL, b, c #define uint32_frombytes_3(a, b, c) a, b, c #define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__) 

    Et avec _Generic:

     const uint8* uint16_tobytes(const uint16* in, uint8* out, size_t bytes); const uint16* uint16_frombytes(uint16* out, const uint8* in, size_t bytes); const uint8* uint32_tobytes(const uint32* in, uint8* out, size_t bytes); const uint32* uint32_frombytes(uint32* out, const uint8* in, size_t bytes); /* The output buffer defaults to NULL if not provided. Generic function name supported on the non-uint8 type, except where said type is unavailable because the argument for output buffer was not provided. */ #include "variadic.h" #define uint16_tobytes_2(a, c) a, NULL, c #define uint16_tobytes_3(a, b, c) a, b, c #define uint16_tobytes(...) VARIADIC( uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__) #define uint16_frombytes_2( b, c) NULL, b, c #define uint16_frombytes_3(a, b, c) a, b, c #define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__) #define uint32_tobytes_2(a, c) a, NULL, c #define uint32_tobytes_3(a, b, c) a, b, c #define uint32_tobytes(...) VARIADIC( uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__) #define uint32_frombytes_2( b, c) NULL, b, c #define uint32_frombytes_3(a, b, c) a, b, c #define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__) #define tobytes(a, ...) _Generic((a), \ const uint16*: uint16_tobytes, \ const uint32*: uint32_tobytes) (VARIADIC2( uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__)) #define frombytes(a, ...) _Generic((a), \ uint16*: uint16_frombytes, \ uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__)) 

    Et avec la sélection du nom de la fonction variadic, qui ne peut pas être combinée avec _Generic:

     // winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments. #define merkle_lamport_5(a, b, c, d, e) a, b, c, d, e #define winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g #define winternitz_5_name() merkle_lamport #define winternitz_7_name() winternitz #define winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__) 

    Oui, vous pouvez faire quelque chose de simulair, ici vous devez connaître les différentes listes d’arguments que vous pouvez obtenir, mais vous avez la même fonction à gérer alors tout.

     typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET; typedef struct{ INPUT_SET type; char* text; } input_set1; typedef struct{ INPUT_SET type; char* text; int var; } input_set2; typedef struct{ INPUT_SET type; int text; } input_set3; typedef union { INPUT_SET type; input_set1 set1; input_set2 set2; input_set3 set3; } MY_INPUT; void my_func(MY_INPUT input) { switch(input.type) { case my_input_set1: break; case my_input_set2: break; case my_input_set3: break; default: // unknown input break; } } 

    OUI

    À travers des macros

    3 parameters:

     #define my_func2(...) my_func3(__VA_ARGS__, 0.5) #define my_func1(...) my_func2(__VA_ARGS__, 10) #define VAR_FUNC(_1, _2, _3, NAME, ...) NAME #define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__) void my_func3(char a, int b, float c) // b=10, c=0.5 { printf("a=%c; b=%d; c=%f\n", a, b, c); } 

    Si vous voulez un 4ème argument, alors un my_func3 supplémentaire doit être ajouté. Notez les modifications dans VAR_FUNC, my_func2 et my_func

    4 parameters:

     #define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added #define my_func2(...) my_func3(__VA_ARGS__, (float)1/2) #define my_func1(...) my_func2(__VA_ARGS__, 10) #define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME #define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__) void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default" { printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d); } 

    Seules les exceptions permettant de faire flotter les variables ne peuvent pas recevoir de valeurs par défaut ( sauf si c'est le dernier argument comme dans le cas des 3 parameters ), car elles nécessitent une période ('.') Qui n'est pas acceptée dans les arguments. Mais peut trouver un travail comme vu dans la macro my_func2 ( de 4 parameters cas )

    Programme

     int main(void) { my_func('a'); my_func('b', 20); my_func('c', 200, 10.5); my_func('d', 2000, 100.5, "hello"); return 0; } 

    Sortie:

     a=a; b=10; c=0.500000; d=default a=b; b=20; c=0.500000; d=default a=c; b=200; c=10.500000; d=default a=d; b=2000; c=100.500000; d=hello 

    Pourquoi ne pouvons-nous pas faire ça?

    Atsortingbuez à l’argument facultatif une valeur par défaut. De cette manière, l’appelant de la fonction n’a pas nécessairement besoin de transmettre la valeur de l’argument. L’argument prend la valeur par défaut. Et facilement cet argument devient facultatif pour le client.

    Par exemple

    void foo (int a, int b = 0);

    Ici b est un argument optionnel.