Reputation: 3481
I'm currently writing a function which will take a variable number of arguments. I pass the number of arguments into the function and then will iterate through the arguments list.
Each of the passed arguments should be an integer. I will be adding this integer to a vector of integers which will be used later.
I would like to make sure that some joker doesn't attempt to pass this function something other then an integer in the future. I recognize that I can check the current argument from va_arg to ensure it is not NULL and I can use something like isanum(va_arg()) to determine if it is a valid integer. I suppose I could even check the sizeof(va_arg) and compare it against the sizeof(int) and ensure they are equal.
Are there any other checks which I can run to verify I have been passed a valid integer?
Thanks in advance for assistance
Upvotes: 7
Views: 3732
Reputation: 61643
Just to illustrate my comment on CygnusX1's answer, you could do it like:
class MyFunction {
std::vector<int> params;
public:
MyFunction() { (*this)(); }
MyFunction(int eatMe) { (*this)(eatMe); }
MyFunction& operator()(int eatMe) {
params.push_back(eatMe);
return *this;
}
void operator()() {
// use params to do something interesting
}
}
MyFunction(2)(3)(5)(7)();
Upvotes: 0
Reputation: 52217
Each of the passed arguments should be an integer. I will be adding this integer to a vector of integers which will be used later.
Then why not just accept a vector of integers?
void AddIntegers(const std::vector<int>& vec);
You can then always concatenate vectors together using iterators.
Or make an interface like this:
void AddInteger(int newInt);
Or even this:
void AddIntegers(const int* integers, unsigned int numIntegers);
template <unsigned int Size>
void AddIntegers(int (&integers)[Size])
{
AddIntegers(integers, Size);
}
int main()
{
int i[] = {1, 2, 3, 4};
AddIntegers(i);
}
These will work if you need to work with a C++03 compiler. If you have a C++0x compiler, there are far superior solutions available.
Upvotes: 3
Reputation: 18572
If you are restricted to C++03 and all your arguments should be integers, one solution would be to simply hide the variable argument function (in a 'detail' namespace for example) and make a series of overloaded functions for 1 to N amount of arguments. Those functions would be simple inline functions that forward the call to the vararg version of the real function. This way, you have one real implementation, no run-time overhead, and you expose a type-safe interface to the caller (and the caller can always use the vararg version if he needs more than N arguments).
Boost.PP can also help to generate these types of repetitive patterns.
Of course, if you have some level of C++0x support, than the problem can be solved in many ways, including initializer_list or variadic templates.
Upvotes: 0
Reputation: 21818
Since you are using C++, how about overloading some operator and pass the arguments one-by-one? For example
class MyFunction {
std::vector<int> param;
public:
MyFunction() { /* some initialisation? */ }
MyFunction &operator,(int eatMe) {
param.push_back(eatMe);
return *this;
}
~MyFunction() {
//the implementation of your function goes here
}
}
Then you can call it like this:
MyFunction(),2,3,5,7;
Note, the use of comma operator may look scary, but it is actually very helpful in this case. It is the lowest possible, left-associative operator.
If your function takes some extra parameters, not only the unknown-length of int
-s, you can pass them in the constructor.
If someone uses something else than int
, the default comma operator will be used (evaluate left side, discard, evaluate right side). If you don't like that - pick a different operator, e.g. stream-like <<
or boost-like %
.
Upvotes: 0
Reputation: 263350
Each of the passed arguments should be an integer.
If you have a C++0x compiler, I suggest an initializer_list<int>
instead of varargs:
#include <initializer_list>
void foo(std::initializer_list<int> numbers)
{
my_vector.insert(my_vector.end(), numbers.begin(), numbers.end());
}
int main()
{
foo( {2, 3, 5, 7} );
}
This is straight-forward and completely type-safe.
Upvotes: 4
Reputation: 96311
You can't do any sort of type checking with varargs. I'd suggest using an iterator range instead (like standard library functions) or possibly a std::vector<int>
. This way the types can't be subverted.
Upvotes: 1
Reputation: 67345
Unfortunately, there really isn't a way to do this. Functions like printf()
can easily be fowled up by passing invalid or the wrong number of arguments.
In C++, this is an advanced feature that requires the programming using such code to ensure the correct arguments are passed.
Upvotes: 1
Reputation: 272792
There is no sensible way you can do this. Variable-argument functions work by concatenating all the raw binary representations of the arguments into one big chunk of data on the stack. So it relies on both the caller and the callee agreeing on what the number and type of arguments are (otherwise you'll end up reading e.g. an int
as if it were a float
).
As to your specific ideas:
va_arg()
is a macro that simply interprets some number of bytes of the raw stack data as whatever type you specify. So invoking sizeof()
on it will simply tell you the size of the data type you asked for.
In general, there are no patterns of raw binary data that form an invalid integer. So the hypothetical isanum()
could not work.
Upvotes: 8
Reputation: 15164
Variable arguments are unsafe by design. You cannot check that the user passed correct type in any way. C++0x comes to the rescue with variadic templates but not many compilers support it nowadays (only GCC afaik).
Upvotes: 1