Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable parameter lists in C++ and C

I'm working on rewriting a C program in C++ to take advantage of OO aspects so it can easily support multiple devices, and one part of the program is an expression evaluator. Expressions can have function calls, and here's the structure for functions.

typedef struct {
    char *name;
    int argc;
    void (*func) ();
} FUNCTION;

Somehow func can have a variable number of arguments passed through it.

RESULT *param[10];

if (Root->Function->argc < 0) {
    /* Function with variable argument list:  */
    /* pass number of arguments as first parameter */
    Root->Function->func(Root->Result, argc, &param);
} else {
    Root->Function->func(Root->Result, param[0], param[1], param[2], param[3], param[4], param[5], param[6],
                         param[7], param[8], param[9]);
}

I'm not even sure how this can be done in C to be honest. An explanation would be excellent. Can it be done in C++?

like image 538
Scott Avatar asked Dec 08 '22 06:12

Scott


2 Answers

The var args are what you are looking for. It works both in C and C++. This is what is used for printf() for example :).

You can find more information by googling va_arg (one of the function used for variable number of arguments)

  • var ags on cpppreference.com
  • var ags on cplusplus.com
like image 114
Thibault Martin-Lagardette Avatar answered Dec 26 '22 06:12

Thibault Martin-Lagardette


Actually I am going to go against everyone here, and your desire.

Ellipsis is wrong. It was thought of as indispensable in C but we learned better since then.

Indeed, there are ways to actually much better in C++, using objects, and for example, function objects.

What you are looking for is the Command Pattern.

Create a base class called 'Command' (interface with a execute() method), then for each of the 'functions' you wanted to put in 'void (*func)()' you create a derived class.

Now, your code is going to look like that:

std::vector<RESULT*> param(10, NULL);

if (Root->Function->argc < 0) {
    /* Function with variable argument list:  */
    /* pass number of arguments as first parameter */
    Command1* aCommand = new Command1(Root->Result);
    aCommand->set(Root->Result, argc, &param);
    Root->Function->command = aCommand;
    Root->Function->command->execute();
} else {
    Command2* aCommand = new Command2(Root->Result);
    aCommand->set(Root->Result, param[0], param[1], param[2], param[3], param[4], param[5], param[6], param[7], param[8], param[9]);
    Root->Function->command = aCommand;
    Root->Function->command->execute();
}

Here you don't need the ellipsis because each command object is specialized and know exactly what are the parameters it needs (number AND types).

The command pattern allow you to have all the benefits of the '...' (ellipsis) without its inconvenient. Well of course some are going to say that it's a loss of time since they don't make mistakes anyway so they don't need to type more... unfortunately I am not that smart so I prefer to define constraints (number, types, assertions) and let the compiler enforce them for me.

like image 44
Matthieu M. Avatar answered Dec 26 '22 06:12

Matthieu M.