Comment réaliser une surcharge de fonction en C?


241

Existe-t-il un moyen d'obtenir une surcharge de fonctions en C? Je regarde des fonctions simples à surcharger comme

foo (int a)  
foo (char b)  
foo (float c , int d)

Je pense qu'il n'y a pas de solution simple; Je recherche des solutions de contournement s'il en existe.


6
Pourquoi voudriez-vous faire ça? C n'a aucune capacité polymorphe. Donc foo (type aléatoire) est impossible. Faites juste de vrais funcs foo_i, foo_ch, foo_d, etc.
jmucchiello

4
Vous pouvez aller dans le mauvais sens en utilisant des pointeurs vides et des identifiants de type.
Alk

11
Je pense que je devrais attirer l'attention sur le fait que la réponse à cette question a changé depuis qu'elle a été posée à l'origine , avec la nouvelle norme C.
Leushenko

Réponses:


127

Il y a peu de possibilités:

  1. fonctions de style printf (tapez comme argument)
  2. fonctions de style opengl (tapez le nom de la fonction)
  3. sous-ensemble c de c ++ (si vous pouvez utiliser un compilateur c ++)

1
pouvez-vous expliquer ou fournir des liens pour les fonctions de style opengl?
FL4SOF du

1
@Lazer: Voici une implémentation simple de fonction de type printf.
Alexey Frunze

12
Non. Printf n'est pas une surcharge de fonction. il utilise vararg !!! Et C ne prend pas en charge la surcharge de fonctions.
hqt

52
@hqt La réponse ne mentionne jamais le mot surcharge.
kyrias

1
@kyrias Si la réponse ne concerne pas la surcharge c'est sur la mauvaise question
Michael Mrozek

233

Oui!

Depuis que cette question a été posée, le standard C (pas d'extensions) a effectivement pris en charge la surcharge de fonctions (pas les opérateurs), grâce à l'ajout du _Genericmot - clé dans C11. (pris en charge dans GCC depuis la version 4.9)

(La surcharge n'est pas vraiment «intégrée» de la manière indiquée dans la question, mais il est très facile d'implémenter quelque chose qui fonctionne comme ça.)

_Genericest un opérateur de compilation dans la même famille que sizeofet _Alignof. Il est décrit dans la section standard 6.5.1.1. Il accepte deux paramètres principaux: une expression (qui ne sera pas évaluée au moment de l'exécution) et une liste d'association type / expression qui ressemble un peu à un switchbloc. _Genericobtient le type global de l'expression puis "bascule" dessus pour sélectionner l'expression de résultat final dans la liste pour son type:

_Generic(1, float: 2.0,
            char *: "2",
            int: 2,
            default: get_two_object());

L'expression ci-dessus est évaluée à 2- le type de l'expression de contrôle est int, elle choisit donc l'expression associée à intcomme valeur. Rien de tout cela ne reste à l'exécution. (La defaultclause est facultative: si vous la laissez désactivée et que le type ne correspond pas, cela provoquera une erreur de compilation.)

La façon dont cela est utile pour la surcharge de fonctions est qu'il peut être inséré par le préprocesseur C et choisir une expression de résultat en fonction du type des arguments passés à la macro de contrôle. Donc (exemple de la norme C):

#define cbrt(X) _Generic((X),                \
                         long double: cbrtl, \
                         default: cbrt,      \
                         float: cbrtf        \
                         )(X)

Cette macro implémente une cbrtopération surchargée , en distribuant le type de l'argument à la macro, en choisissant une fonction d'implémentation appropriée, puis en passant l'argument de macro d'origine à cette fonction.

Donc, pour implémenter votre exemple d'origine, nous pourrions faire ceci:

foo_int (int a)  
foo_char (char b)  
foo_float_int (float c , int d)

#define foo(_1, ...) _Generic((_1),                                  \
                              int: foo_int,                          \
                              char: foo_char,                        \
                              float: _Generic((FIRST(__VA_ARGS__,)), \
                                     int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A

Dans ce cas, nous aurions pu utiliser une default:association pour le troisième cas, mais cela ne montre pas comment étendre le principe à plusieurs arguments. Le résultat final est que vous pouvez utiliser foo(...)dans votre code sans vous soucier (beaucoup [1]) du type de ses arguments.


Pour les situations plus compliquées, par exemple les fonctions surchargeant un plus grand nombre d'arguments ou des nombres variables, vous pouvez utiliser des macros utilitaires pour générer automatiquement des structures de répartition statiques:

void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }

#define print(...) OVERLOAD(print, (__VA_ARGS__), \
    (print_ii, (int, int)), \
    (print_di, (double, int)), \
    (print_iii, (int, int, int)) \
)

#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"

int main(void) {
    print(44, 47);   // prints "int, int"
    print(4.4, 47);  // prints "double, int"
    print(1, 2, 3);  // prints "int, int, int"
    print("");       // prints "unknown arguments"
}

( implémentation ici ) Donc, avec un peu d'effort, vous pouvez réduire la quantité de passe-partout pour ressembler à peu près à une langue avec un support natif pour la surcharge.

Soit dit en passant, il était déjà possible de surcharger le nombre d'arguments (pas le type) en C99.


[1] notez cependant que la façon dont C évalue les types peut vous tromper. Cela va choisir foo_intsi vous essayez de lui passer un littéral de caractère, par exemple, et vous devez vous soucier un peu si vous voulez que vos surcharges prennent en charge les littéraux de chaîne. Dans l'ensemble, c'est plutôt cool.


Sur la base de votre exemple, il semble que la seule chose surchargée soit une fonction comme les macros. Voyons si je comprends bien: si vous voulez surcharger des fonctions, vous utiliserez simplement le préprocesseur pour détourner l'appel de fonction en fonction des types de données transmis, non?
Nick

Hélas, chaque fois que C11 commence à faire son chemin, je suppose que MISRA n'embrassera pas cette fonctionnalité pour les mêmes raisons qu'ils interdisent les listes d'arguments variables. J'essaie de rester proche de MISRA dans mon monde.
Nick

9
@Nick c'est la surcharge. Il est simplement géré implicitement dans d'autres langages (par exemple, vous ne pouvez pas vraiment obtenir "un pointeur vers une fonction surchargée" dans n'importe quel langage, car la surcharge implique plusieurs corps). Notez que cela ne peut pas être fait uniquement par le préprocesseur, cela nécessite une répartition de type quelconque; le préprocesseur change simplement son apparence.
Leushenko

1
Comme quelqu'un qui est assez familier avec C99 et veut apprendre à faire cela, cela semble trop compliqué, même pour C.
Tyler Crompton

5
@TylerCrompton Il est évalué au moment de la compilation.
JAB

75

Comme déjà indiqué, la surcharge dans le sens où vous voulez dire n'est pas prise en charge par C. Un idiome courant pour résoudre le problème est de faire accepter à la fonction une union étiquetée . Ceci est implémenté par un structparamètre, où structlui - même se compose d'une sorte d'indicateur de type, comme un enum, et d'un uniondes différents types de valeurs. Exemple:

#include <stdio.h>

typedef enum {
    T_INT,
    T_FLOAT,
    T_CHAR,
} my_type;

typedef struct {
    my_type type;
    union {
        int a; 
        float b; 
        char c;
    } my_union;
} my_struct;

void set_overload (my_struct *whatever) 
{
    switch (whatever->type) 
    {
        case T_INT:
            whatever->my_union.a = 1;
            break;
        case T_FLOAT:
            whatever->my_union.b = 2.0;
            break;
        case T_CHAR:
            whatever->my_union.c = '3';
    }
}

void printf_overload (my_struct *whatever) {
    switch (whatever->type) 
    {
        case T_INT:
            printf("%d\n", whatever->my_union.a);
            break;
        case T_FLOAT:
            printf("%f\n", whatever->my_union.b);
            break;
        case T_CHAR:
            printf("%c\n", whatever->my_union.c);
            break;
    }

}

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

    s.type=T_INT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_FLOAT;
    set_overload(&s);
    printf_overload(&s);

    s.type=T_CHAR;
    set_overload(&s);
    printf_overload(&s); 
}

22
Pourquoi ne pas vous faire juste tous les whatevers en fonctions séparées ( set_int, set_float, etc.). Ensuite, "baliser avec le type" devient "ajouter le nom du type au nom de la fonction". La version de cette réponse implique plus de frappe, plus de coûts d'exécution, plus de risques d'erreurs qui ne seront pas détectées au moment de la compilation ... Je ne vois aucun avantage à faire les choses de cette façon! 16 votes positifs?!
Ben

20
Ben, cette réponse est votée parce qu'elle répond à la question, au lieu de simplement dire «ne fais pas ça». Vous avez raison, il est plus idiomatique en C d'utiliser des fonctions séparées, mais si l'on veut du polymorphisme en C, c'est une bonne façon de le faire. De plus, cette réponse montre comment vous implémenteriez le polymorphisme d'exécution dans un compilateur ou une machine virtuelle: étiquetez la valeur avec un type, puis distribuez-la en fonction de cela. C'est donc une excellente réponse à la question initiale.
Nils von Barth

20

Voici l'exemple le plus clair et le plus concis que j'ai trouvé démontrant la surcharge de fonctions en C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int addi(int a, int b) {
    return a + b;
}

char *adds(char *a, char *b) {
    char *res = malloc(strlen(a) + strlen(b) + 1);
    strcpy(res, a);
    strcat(res, b);
    return res;
}

#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)

int main(void) {
    int a = 1, b = 2;
    printf("%d\n", add(a, b)); // 3

    char *c = "hello ", *d = "world";
    printf("%s\n", add(c, d)); // hello world

    return 0;
}

https://gist.github.com/barosl/e0af4a92b2b8cabd05a7


1
Je pense que c'est une dupe de stackoverflow.com/a/25026358/1240268 dans l'esprit (mais avec moins d'explications).
Andy Hayden

1
Je préfère définitivement 1 seul bloc continu de code complet et exécutable à la côtelette de découpage et de découpe qui est # 1240268. À chacun son bonheur.
Jay Taylor

1
Je préfère les réponses qui expliquent ce qu'elles font et pourquoi elles fonctionnent. Cela ne fait ni l'un ni l'autre. "Le meilleur que j'aie jamais vu:" n'est pas une exposition.
underscore_d

19

Si votre compilateur est gcc et que cela ne vous dérange pas de faire des mises à jour à la main chaque fois que vous ajoutez une nouvelle surcharge, vous pouvez faire de la magie de macro et obtenir le résultat que vous voulez en termes d'appels, ce n'est pas aussi agréable à écrire ... mais c'est possible

regardez __builtin_types_compatible_p, puis utilisez-le pour définir une macro qui fait quelque chose comme

#define foo(a) \
((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

mais oui méchant, ne le faites pas

EDIT: C1X obtiendra un support pour les expressions génériques de type, elles ressemblent à ceci:

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

13

Oui, en quelque sorte.

Voici l'exemple:

void printA(int a){
printf("Hello world from printA : %d\n",a);
}

void printB(const char *buff){
printf("Hello world from printB : %s\n",buff);
}

#define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 
#define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define _Num_ARGS_(...) __VA_ARG_N(__VA_ARGS__) 
#define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) 
#define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args)>t)
#define CHECK_ARGS_MIN_LIMIT(t) if(NUM_ARGS(args) 
#define print(x , args ...) \
CHECK_ARGS_MIN_LIMIT(1) printf("error");fflush(stdout); \
CHECK_ARGS_MAX_LIMIT(4) printf("error");fflush(stdout); \
({ \
if (__builtin_types_compatible_p (typeof (x), int)) \
printA(x, ##args); \
else \
printB (x,##args); \
})

int main(int argc, char** argv) {
    int a=0;
    print(a);
    print("hello");
    return (EXIT_SUCCESS);
}

Il affichera 0 et bonjour .. à partir de printA et printB.


2
int main (int argc, char ** argv) {int a = 0; impression (a); imprimer ("bonjour"); retour (EXIT_SUCCESS); } affichera 0 et bonjour .. à partir de printA et printB ...
Captain Barbossa

1
__builtin_types_compatible_p, ce compilateur GCC n'est-il pas spécifique?
Sogartar

11

L'approche suivante est similaire à celle de a2800276 , mais avec un peu de magie de macro C99 ajoutée:

// we need `size_t`
#include <stddef.h>

// argument types to accept
enum sum_arg_types { SUM_LONG, SUM_ULONG, SUM_DOUBLE };

// a structure to hold an argument
struct sum_arg
{
    enum sum_arg_types type;
    union
    {
        long as_long;
        unsigned long as_ulong;
        double as_double;
    } value;
};

// determine an array's size
#define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY)))

// this is how our function will be called
#define sum(...) _sum(count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__))

// create an array of `struct sum_arg`
#define sum_args(...) ((struct sum_arg []){ __VA_ARGS__ })

// create initializers for the arguments
#define sum_long(VALUE) { SUM_LONG, { .as_long = (VALUE) } }
#define sum_ulong(VALUE) { SUM_ULONG, { .as_ulong = (VALUE) } }
#define sum_double(VALUE) { SUM_DOUBLE, { .as_double = (VALUE) } }

// our polymorphic function
long double _sum(size_t count, struct sum_arg * args)
{
    long double value = 0;

    for(size_t i = 0; i < count; ++i)
    {
        switch(args[i].type)
        {
            case SUM_LONG:
            value += args[i].value.as_long;
            break;

            case SUM_ULONG:
            value += args[i].value.as_ulong;
            break;

            case SUM_DOUBLE:
            value += args[i].value.as_double;
            break;
        }
    }

    return value;
}

// let's see if it works

#include <stdio.h>

int main()
{
    unsigned long foo = -1;
    long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10));
    printf("%Le\n", value);
    return 0;
}

11

Cela peut ne pas aider du tout, mais si vous utilisez clang, vous pouvez utiliser l'attribut surchargeable - Cela fonctionne même lors de la compilation en C

http://clang.llvm.org/docs/AttributeReference.html#overloadable

Entête

extern void DecodeImageNow(CGImageRef image, CGContextRef usingContext) __attribute__((overloadable));
extern void DecodeImageNow(CGImageRef image) __attribute__((overloadable));

la mise en oeuvre

void __attribute__((overloadable)) DecodeImageNow(CGImageRef image, CGContextRef usingContext { ... }
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image) { ... }

10

Dans le sens que vous voulez dire - non, vous ne pouvez pas.

Vous pouvez déclarer une va_argfonction comme

void my_func(char* format, ...);

, mais vous devrez transmettre une sorte d'informations sur le nombre de variables et leurs types dans le premier argument - comme le printf()fait le fait.


6

Normalement, une verrue pour indiquer le type est ajoutée ou ajoutée au nom. Vous pouvez vous en tirer avec les macros dans certains cas, mais cela dépend plutôt de ce que vous essayez de faire. Il n'y a pas de polymorphisme en C, seulement de la coercition.

Des opérations génériques simples peuvent être effectuées avec des macros:

#define max(x,y) ((x)>(y)?(x):(y))

Si votre compilateur prend en charge typeof , des opérations plus compliquées peuvent être placées dans la macro. Vous pouvez alors avoir le symbole foo (x) pour prendre en charge la même opération de différents types, mais vous ne pouvez pas faire varier le comportement entre différentes surcharges. Si vous voulez des fonctions réelles plutôt que des macros, vous pourrez peut-être coller le type au nom et utiliser un second collage pour y accéder (je n'ai pas essayé).


pouvez-vous expliquer un peu plus l'approche macro?
FL4SOF

4

La réponse de Leushenko est vraiment cool - uniquement: l' fooexemple ne compile pas avec GCC, qui échoue foo(7), trébuchant sur la FIRSTmacro et l'appel de fonction réel ( (_1, __VA_ARGS__), restant avec une virgule excédentaire. De plus, nous sommes en difficulté si nous voulons fournir des surcharges supplémentaires , tel quefoo(double) .

J'ai donc décidé d'élaborer un peu plus la réponse, notamment pour permettre une surcharge de vide (foo(void) - ce qui a causé pas mal de problèmes ...).

L'idée est maintenant: Définissez plus d'un générique dans différentes macros et laissez sélectionner le bon en fonction du nombre d'arguments!

Le nombre d'arguments est assez facile, basé sur cette réponse :

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

C'est bien, nous résolvons l'un SELECT_1ou l' autre SELECT_2(ou plusieurs arguments, si vous le souhaitez / en avez besoin), nous avons donc simplement besoin de définitions appropriées:

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1),    \
        int: foo_int,                   \
        char: foo_char,                 \
        double: foo_double              \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

OK, j'ai déjà ajouté la surcharge void - cependant, celle-ci n'est en fait pas couverte par la norme C, ce qui ne permet pas d'arguments variadiques vides, c'est-à-dire que nous comptons alors sur des extensions de compilateur !

Au tout début, un appel de macro vide ( foo()) produit toujours un jeton, mais vide. Ainsi, la macro de comptage renvoie en fait 1 au lieu de 0 même lors d'un appel de macro vide. Nous pouvons "facilement" éliminer ce problème, si nous plaçons la virgule après__VA_ARGS__ conditionnellement , selon que la liste est vide ou non:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

Cela semblait facile, mais la COMMAmacro est assez lourde; heureusement, le sujet est déjà traité dans un blog de Jens Gustedt (merci Jens). L'astuce de base est que les macros de fonction ne sont pas développées si elles ne sont pas suivies de parenthèses, pour plus d'explications, jetez un œil au blog de Jens ... Il suffit de modifier un peu les macros selon nos besoins (je vais utiliser des noms plus courts et moins d'arguments pour la brièveté).

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,

Et maintenant nous allons bien ...

Le code complet en un seul bloc:

/*
 * demo.c
 *
 *  Created on: 2017-09-14
 *      Author: sboehler
 */

#include <stdio.h>

void foo_void(void)
{
    puts("void");
}
void foo_int(int c)
{
    printf("int: %d\n", c);
}
void foo_char(char c)
{
    printf("char: %c\n", c);
}
void foo_double(double c)
{
    printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
    printf("double: %.2f, int: %d\n", c, d);
}

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
        int: foo_int,                \
        char: foo_char,              \
        double: foo_double           \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N

#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,

int main(int argc, char** argv)
{
    foo();
    foo(7);
    foo(10.12);
    foo(12.10, 7);
    foo((char)'s');

    return 0;
}

1

Ne pouvez-vous pas simplement utiliser C ++ et ne pas utiliser toutes les autres fonctionnalités C ++ sauf celle-ci?

S'il n'y a toujours pas de C strict, je recommanderais plutôt les fonctions variadiques .


3
Pas si un compilateur C ++ n'est pas disponible pour le système d'exploitation pour lequel il code.
Brian

2
non seulement cela, mais il voudra peut-être un C ABI qui ne contient pas de nom.
Spudd86


-4

J'espère que le code ci-dessous vous aidera à comprendre la surcharge de fonctions

#include <stdio.h>
#include<stdarg.h>

int fun(int a, ...);
int main(int argc, char *argv[]){
   fun(1,10);
   fun(2,"cquestionbank");
   return 0;
}
int fun(int a, ...){
  va_list vl;
  va_start(vl,a);

  if(a==1)
      printf("%d",va_arg(vl,int));
   else
      printf("\n%s",va_arg(vl,char *));
}

2
Une réponse devrait expliquer ce qu'elle fait et pourquoi cela fonctionne. Si ce n'est pas le cas, comment peut-il aider quelqu'un à comprendre quoi que ce soit?
underscore_d

Il n'y a pas de surcharge ici.
melpomene

va_end n'a jamais été appelé
user2262111
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.