Checking the arguments of a variable of the expected type

I am currently writing a function that takes a variable number of arguments. I pass the number of arguments to a function, and then iterates over the argument list.

Each of the arguments passed must be an integer. I will add this integer to the integer vector, which will be used later.

I would like to make sure that some joker is not trying to pass something else to this function, and then an integer in the future. I understand that I can check the current argument from va_arg to make sure that it is not NULL, and I can use something like isanum (va_arg ()) to determine if it is a real integer. I suppose I can even check sizeof (va_arg) and compare it with sizeof (int) and make sure they are equal.

Are there any other checks that I can run to verify that I have been given a real integer?

Thank you in advance for your help.

+6
source share
9 answers

There is no reasonable way to do this. Variable-arguments functions work by combining all raw binary representations of arguments into one large piece of data on the stack. Thus, he relies on both the caller and the called party, agreeing that such a number and type of arguments (otherwise you end reading, for example, int , as if it were a float ).

As for your specific ideas:

  • va_arg() is a macro that simply interprets a certain number of bytes of data from the source stack as any type that you specify. Thus, by calling sizeof() , it will just tell you the size of the requested data type.

  • In general, there are no raw binary data patterns that form an invalid integer. So the hypothetical isanum() could not work.

+8
source

Each of the arguments passed must be an integer.

If you have a C ++ 0x compiler, I suggest 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} ); } 

It is straightforward and completely safe.

+4
source

Each of the arguments passed must be an integer. I will add this integer with a vector of integers, which will be used later.

Then why not just accept the integer vector?

 void AddIntegers(const std::vector<int>& vec); 

Then you can always combine vectors together with iterators.

Or create 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); } 

They will work if you need to work with a C ++ 03 compiler. If you have a C ++ 0x compiler, there are much better solutions.

+3
source

Variable arguments are unsafe by design. You cannot verify that the user has passed the correct type in any way. C ++ 0x comes to the rescue with variable templates, but not many compilers currently support it (only GCC afaik).

+1
source

Unfortunately, there really is no way to do this. Functions such as printf() can be easily cracked by passing an invalid or incorrect number of arguments.

In C ++, this is an advanced function that requires programming using such code to ensure that the correct arguments are accepted.

+1
source

You cannot perform any type checks with varargs. Instead, I would suggest using a range of iterators (e.g. standard library functions) or perhaps std::vector<int> . Thus, types cannot be undermined.

+1
source

Since you are using C ++, how about overloading some operator and passing arguments one by one? for instance

 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. Using a comma operator may look scary, but in this case it is really very useful. This is the smallest possible left-associative operator.

If your function accepts additional parameters, and not just an unknown length of int -s, you can pass them in the constructor.

If someone uses something other than int , the default comma operator will be used (evaluate the left side, discard, evaluate the right side). If you do not like it, choose another operator, for example. stream-like << or boost-like % .

0
source

If you are limited to C ++ 03, and all your arguments must be integers, one solution would be to simply hide the variable argument function (for example, in the "detail" namespace) and make a series of overloaded functions for 1 to N number of arguments. These functions would be simple built-in functions that forward the call to the vararg version of the real function. That way you have one real implementation, no run-time overhead, and you provide the caller with a safe type (and the caller can always use the vararg version if it needs more than N arguments).

Boost.PP can also help generate these types of repeating patterns.

Of course, if you have some level of C ++ 0x support, then the problem can be solved in many ways, including initializer_list or variadic templates.

0
source

To illustrate my comment on CygnusX1's answer, you can 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)(); 
0
source

Source: https://habr.com/ru/post/891308/


All Articles