开发者

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 开发者_StackOverflow中文版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++?


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


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.


You can do it in C++ by using ... in the argument list. Take a look here for explanation. But I think it will be better if you just pass a vector of param to your function.


Remember, a function is just another address, that when called start take input parameters from the stack. So, when you declare a FUNCTION, you are just storing the address of that function. With that address the function could be called later with the different parameters. An yes, it could be done in C++ by using argument lists, however the implementation depends on how you want it to handle it.


Personally, when it comes to evaluating the AST for an expression, I tend to use a domain specific language to generate a heirarchy of AST node structs and a set of multiple-dispatch operations for me. I have my own DSL for this, but the idea was stolen from treecc. A lot of problems go away because you don't need to have a single node class or a single evaluate-node function - and with the DSL doing various checks for you, you mostly avoid replacing those problems with different ones.

treecc can generate C or C++ output (or a few other languages) IIRC. It's a lot like using flex or bison. Generated code is a bit naive and you soon end up wishing for more control over inheritance, but it works pretty well.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜