Verifying variable arguments are of expected type

淺唱寂寞╮ 提交于 2019-12-04 06:23:48

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.

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.

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.

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).

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.

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.

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 %.

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.

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)();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!