Блог пользователя Edvard

Автор Edvard, 12 лет назад, По-русски

Всем привет. Хочу написать функцию которой передается количество аргументов и аргументы возможно разного типа. Например, чтобы можно было вызвать функцию примерно так print(3, 1, 1.9, "abc"). Погуглил немного ничего для определения типа не нашел может кто знает как это сделать? Если для параметров тип заранее известен то можно так:

// ...
#include <cstdarg>
// ...
inline void print(int n, ...)
{
	va_list params;
	va_start(params, n);
	
	if (n == 1)
	{
		int x = va_arg(params, int);
		cerr << "int " << x << endl;
	}
	else
	{
		char* x = va_arg(params, char*);
		cerr << "string " << x << endl;
	}
	
	va_end(params);
}
// ...

UPD: Всем спасибо за ответы. Результат видимо такой: в обычном стандарте С++ нормально это не делается, а в С++11 есть свои фишки о которых написал cmd

  • Проголосовать: нравится
  • +3
  • Проголосовать: не нравится

»
12 лет назад, # |
  Проголосовать: нравится +4 Проголосовать: не нравится

Забавно, минут 10 назад это обсуждали.

Такие функции работают методом "положили все на стек, а дальше разбирайся сам". Насколько я понимаю никакой информации про тип при этом не сохраняется. В частности scanf/printf поэтому берут его из format_string.

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Это вроде бы не возможно. Собственно, потому что мы ничего не знаем о полученных аргументах, мы и должны передавать сперва инфрмацию: кол-во аргументов, формат для printf'a или т.п.

»
12 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится

Буквально вчера почти из-за этого бросил попытки написать проект на С++ и сел изучать Java. Присоединяюсь к ораторам выше — на С++ это сделать невозможно.

»
12 лет назад, # |
Rev. 2   Проголосовать: нравится +39 Проголосовать: не нравится

Можно сделать на variadic templates которые появились в C++11. Пример c printf и variadic templates

Если лишнее убрать, то получится: http://ideone.com/s1LlH

UPD: вариант с выводом некоторых типов: http://ideone.com/9nBfZ.

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

На самом деле можно обойти тот факт, что аргументы могут быть разного типа, заменить их на union. Тогда понятно, что будет лежать в стеке. Например вот так . Печать правда не будет выглядить так хорошо, но ведь все равно так можно делать при желании.

P.S. Кто-то знает, почему оно на ideone.com выдаёт SIGILL? На моём компьютере все работает прекрасно.

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Можно сделать замечание, что объекты в стеке не обязательно расположены так, что (&n)+1 или i++ будет работать. То есть, их всё равно нужно доставать через va_arg. Однако это не исправляет SIGILL :-)

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +13 Проголосовать: не нравится

    На Stack Overflow ответили, что эта программа на самом деле вызывает undefined behavior.

    Ссылки на стандарт C++ 2003:

    • В 5.2.2.7 указано, что аргумент, передаваемый через ..., должен быть POD (Plain Old Data).
    • В 9.0.4 указано, что POD-структура есть aggregate.
    • В 8.5.1.1 указано, что aggregate-типы не имеют пользовательских конструкторов.
    • А union multytype таковые имеет → FAIL.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Странно... Ну раз так по стандарту делать не надо, то ладно. Буду знать.

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Только сейчас подумал, а зачем нужен конструктор, когда можно все сделать C-образным.

      http://ideone.com/bSzz7

      Так оно работает :)

      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Развивая идею, можно ещё и избавиться от mt() в вызове print и вообще от varargs: http://ideone.com/isFEo

        Правда, требует __VA_ARGS__, которого нет в C++98/03.