Проверяемые переменные аргументы имеют ожидаемый тип - PullRequest
6 голосов
/ 24 июня 2011

Я сейчас пишу функцию, которая будет принимать переменное количество аргументов. Я передаю количество аргументов в функцию, а затем перебираю список аргументов.

Каждый из переданных аргументов должен быть целым числом. Я добавлю это целое число к вектору целых чисел, который будет использован позже.

Я хотел бы убедиться, что какой-то джокер не попытается передать этой функции что-то другое, чем целое число в будущем. Я понимаю, что могу проверить текущий аргумент из va_arg, чтобы убедиться, что он не равен NULL, и я могу использовать что-то вроде isanum (va_arg ()), чтобы определить, является ли оно действительным целым числом. Я полагаю, я мог бы даже проверить sizeof (va_arg) и сравнить его с sizeof (int) и убедиться, что они равны.

Существуют ли другие проверки, которые я могу выполнить, чтобы убедиться, что мне передано правильное целое число?

Заранее спасибо за помощь

Ответы [ 9 ]

8 голосов
/ 24 июня 2011

Нет разумного способа сделать это.Функции с переменными аргументами работают путем объединения всех необработанных двоичных представлений аргументов в один большой кусок данных в стеке.Таким образом, он полагается как на вызывающего, так и на вызываемого абонента, который согласен с количеством и типом аргументов (в противном случае вы, в конечном итоге, прочитаете, например, int, как если бы это было float).

Какк вашим конкретным идеям:

  • va_arg() - это макрос, который просто интерпретирует некоторое количество байтов необработанных данных стека как любой тип, который вы укажете.Так что вызов sizeof() для него просто скажет вам размер запрашиваемого вами типа данных.

  • В общем, нет шаблонов необработанных двоичных данных, которые образуют недопустимое целое число.Таким образом, гипотетический isanum() не может работать.

4 голосов
/ 24 июня 2011

Каждый из переданных аргументов должен быть целым числом.

Если у вас есть компилятор C ++ 0x, я предлагаю вместо * varargs initializer_list<int>:

#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} );
}

Это простой и полностью безопасный тип.

3 голосов
/ 24 июня 2011

Каждый из переданных аргументов должен быть целым числом.Я добавлю это целое число к вектору целых чисел, который будет использоваться позже.

Тогда почему бы просто не принять вектор целых чисел?

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

Затем можно всегда объединитьвекторы вместе с использованием итераторов.

Или создайте такой интерфейс:

void AddInteger(int newInt);

Или даже так:

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

Они будут работать, если вам нужно работать сКомпилятор C ++ 03.Если у вас есть компилятор C ++ 0x, есть гораздо лучшие решения.

1 голос
/ 24 июня 2011

Вы не можете выполнять какую-либо проверку типов с помощью varargs. Я бы предложил вместо этого использовать диапазон итераторов (например, стандартные библиотечные функции) или, возможно, std::vector<int>. Таким образом, типы не могут быть подорваны.

1 голос
/ 24 июня 2011

К сожалению, действительно нет способа сделать это.Такие функции, как printf(), можно легко вызвать, передав недопустимое или неправильное количество аргументов.

В C ++ это расширенная функция, которая требует программирования с использованием такого кода для обеспечения передачи правильных аргументов.

1 голос
/ 24 июня 2011

Переменные аргументы небезопасны.Вы не можете проверить, что пользователь передал правильный тип в любом случае.C ++ 0x приходит на помощь с переменными шаблонами, но в настоящее время не многие компиляторы поддерживают его (только GCC afaik).

0 голосов
/ 25 июня 2011

Просто, чтобы проиллюстрировать мой комментарий к ответу CygnusX1, вы можете сделать это следующим образом:

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 голосов
/ 24 июня 2011

Если вы ограничены C ++ 03 и все ваши аргументы должны быть целыми числами, одним из решений будет просто скрыть функцию переменного аргумента (например, в пространстве имен detail) и создать серию перегруженных функций для 1 N количество аргументов. Эти функции будут простыми встроенными функциями, которые перенаправляют вызов vararg-версии реальной функции. Таким образом, у вас есть одна реальная реализация, без затрат времени выполнения, и вы предоставляете вызывающему интерфейсу безопасный тип (и вызывающий всегда может использовать версию vararg, если ему нужно больше, чем N аргументов).

Boost.PP также может помочь генерировать эти типы повторяющихся шаблонов.

Конечно, если у вас есть некоторый уровень поддержки C ++ 0x, тогда эту проблему можно решить разными способами, включая initializer_list или шаблоны с переменным числом аргументов.

0 голосов
/ 24 июня 2011

Поскольку вы используете C ++, как насчет перегрузки некоторого оператора и передачи аргументов один за другим?Например,

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
  }
}

Затем вы можете назвать это так:

MyFunction(),2,3,5,7;

Обратите внимание, использование оператора запятой может выглядеть страшно, но на самом деле это очень полезно в этом случае.Это наименьший из возможных левоассоциативных операторов.

Если ваша функция принимает некоторые дополнительные параметры, а не только неизвестную длину int -s, вы можете передать их в конструкторе.

Если кто-то использует что-то еще, кроме int, будет использоваться оператор запятой по умолчанию (оценка левой стороны, сброс, оценка правой стороны).Если вам это не нравится - выберите другого оператора, например, в виде потока << или в стиле boost %.

...