I need to define a virtual function that can take variable number of arguments, problem is c style ellipses does not work for non pod types, I have limited amount of memory (2KB) so i am trying to avoid allocating temp object just to pass to the function, all arguments will be of the same type (a custom shared pointer), I also don't have access to stl or boost. Is there a c++ trick that would allow me to call a func with variable arguments?
-
Do you need to pass them all to the same function call? Or could a chained call work, i.e. object(arg1)(arg2)(arg3)...(argN) ? – Pablo Apr 27 '11 at 16:20
5 Answers
Assuming your argument types are of class Arg
, you can try this:
class ArgUser {
public:
// syntactic sugar:
void method() { // nullary
doMethod();
}
void method( const Arg & a1 ) { // unary
doMethod( &a1 );
}
void method( const Arg & a1, const Arg & a2 ) { // binary
doMethod( &a1, &a2 );
}
// and so on, until max arity
private:
// actual virtual function:
virtual void doMethod( const Arg * a1=0, const Arg * a2=0 /*etc, until max arity */ ) = 0;
};
This solution has the following properties:
- It uses the NVI idiom
- It uses pointers because they will not cause temporaries to be created, even for unused default arguments.
- It encapsulates the ugly pointer juggling in (inline) wrapper methods.
An alternative solution (that may or may not be more efficient) is this:
class AltArgUser {
public:
// syntactic sugar:
void method() { // nullary
doMethod( 0, 0 );
}
void method( const Arg & a1 ) { // unary
doMethod( &&a1, 1 );
}
void method( const Arg & a1, const Arg & a2 ) { // binary
const Arg * args[] = { &a1, &a2 };
doMethod( args, 2 );
}
// and so on, until max arity
private:
// actual virtual function:
virtual void doMethod( const Arg * args[], size_t numArgs ) = 0;
};
To decide which one to use, you need to study the assembler generated for each method on your particular platform. Whatever you choose, you should definitely keep the wrapper functions.

- 24,485
- 12
- 80
- 90
Define the function to take a pointer to an array of parameters and a parameter for the size of the array.
Also, if you do not want to hard code a fixed sized array you could use alloca
to allocate the storage on the stack and not worry about a trip to the heap or calling free
.

- 8,002
- 3
- 33
- 52
Pointers to shared pointers are PODs, could you change the prototype to use the memory location of each argument? Like this (not tested):
shared_ptr arg1;
shared_ptr arg2;
ClassWithVirtualFunction c;
c.yourVirtualFunction(&arg1, &arg2, NULL);
ClassWithVirtualFunction
{
virtual void yourVirtualFunction(shared_ptr* first, ...)
{
va_list marker;
va_start( marker, first );
shared_ptr* current=first;
while (current != NULL)
{
/* do stuff with *current */
current = va_arg( marker, shared_ptr* );
}
va_end(marker);
}
}

- 1,639
- 11
- 19
-
Why can you be certain that there will be a NULL pointer following all the arguments? From what I can see, you use that condition to determine when to stop reading the args. Thnx. – penelope Mar 30 '12 at 10:17
You can use a fixed array that is actually faster and takes less space than variable number of arguments. Because variable number of arguments does a lot of checks and safety.
mytype arr[3];
arr[0] = a;
// etc
myfunction(arr, 3);

- 30,896
- 18
- 85
- 139
-
3"checks and safety" definitely does NOT describe C++ variable argument lists. – Ben Voigt Apr 27 '11 at 16:13
-
Perhaps you meant "requires a lot of checks and safety precautions" ? – daramarak Apr 27 '11 at 16:47
Perhaps if you already have your own custom pointer type, you could define your own basic linked list class (this is C++ after all), with the last "pointer to next" being NULL to indicate the end of the variable number of args.
Something like this:
class MyPointer
{
// Whatever you have/want
};
class MyArgs
{
static int nargs; // Static class variable for total number of args
MyPointer* data;
MyArgs* next;
public:
MyArgs(int nargs)
{
// Some funky constructor to create required number of args...
}
MyPointer* operator[](int at)
{
// Nice overloaded operator for you to get at data
}
};
class PlanToDoStuff
{
public:
virtual void foobar(MyArgs)=0;
};
class ActuallyDoStuff: public PlanToDoStuff
{
public:
void foobar(MyArgs)
{
// Do stuff with args...
}
};
int main()
{
MyArgs args(3);
ActuallyDoStuff dosomething;
dosomething.foobar(args);
}

- 2,694
- 21
- 35