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

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

Сел почитать 4-е издание Страуструпа и наткнулся на функцию, которую удобно использовать для отладочного вывода, если надоело постоянное << ' ' << между переменными в выражениях вроде cerr << variable1 << ' ' << variable2 << ' ' << variable3 << endl; и хочется немного лучшей читабельности.

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

void dout() { cerr << endl; }

template <typename Head, typename... Tail>
void dout(Head H, Tail... T) {
  cerr << H << ' ';
  dout(T...);
}

Пример использования (str == "ololo"):

dout(123,str,4.0);
dout();
dout(1,2);

/* Prints:
123 ololo 4

1 2
*/

То же самое обычным способом:

cerr << 123 << ' ' << str << ' ' << 4.0 << endl;
cerr << endl;
cerr << 1 << ' ' << 2 << endl;
  • Проголосовать: нравится
  • +137
  • Проголосовать: не нравится

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

Прям Prolog какой-то, здорово, спасибо)

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

Для предпочитающих C-style ввод-вывод вместо потоков есть не менее приятный макрос, также утянутый мною откуда-то из документации по gcc:

#define eprintf(...) fprintf(stderr, __VA_ARGS__)
...
eprintf("n = %d, m = %d, time=%.2lf\n", n, m, clock() / (double)CLOCKS_PER_SEC);

Также удобно его сразу дефайнить, чтобы он компилировался на сервере ни во что:

#ifdef LOCAL
    #define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
    #define eprintf(...) 42 
#endif

(да, если кто не знает, оператор 42; — корректный оператор, который ничего не делает)

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

    Если что, то ;; тоже вполне корректно и можно дефайнить в пустоту.

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

      Твой вариант хуже работает с записями вида:

      if (x == 3)
          y += 5, eprintf("new value of y: %d\n", y), was = true;
      

      А я большой любитель пописать выражения через запятые. Моё раскроется в синтаксически корректное:

      if (x == 3)
          y += 5, 42, was = true;
      

      А твоё в синтаксически некорректное:

      if (x == 3)
          y += 5, ;;, was = true;
      
      • »
        »
        »
        »
        11 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Эм, я имел ввиду все-таки в пустоту, но да, согласен, получится все равно не корректно

        y += 5, , was = true;
      • »
        »
        »
        »
        11 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится +9 Проголосовать: не нравится

        Лучше тогда уж ((void)42), чтобы это 42 нигде не использовать случайно.

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

        Ура! я нашел еще одного человека который не любит писать лишние скобочки пишет через запятую!

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

    Вместо такого обычно пишут

    #define eprintf(...)  static_cast<void>(0)

    Этот вариант ещё поймает и попытку использовать результат вызова eprintf().

    EDIT: Ой, PavelKunyavskiy это уже написал.

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

Ещё, пожалуй, тут стоит упомянуть один удобный макрос:

#define DB(x) cerr<<#x<<"="<<(x)<<"\n"

Он выводит то, что ему передаётся, в текстовом виде и само значение.

Например:

int x = 5, y = 10;

DB(x);
DB(y);
DB(x+y);
DB(x*y+2);

/* Prints:
x=5
y=10
x+y=15
x*y+2=52

*/

В таком логе гораздо удобнее ориентироваться, чем просто в наборе значений. И между тем экономится немало времени, по сравнению если б мы писали комментарии к логу вручную: cerr << "x=" << x << endl;

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

    Изрядно поизвращавшись, можно заставить этот макрос работать с переменным (но ограниченным) числом параметров. То-есть мы вызовем DBN(x, y, x*y+2); и получим x=5 y=10 x*y+2=52

    Решение, работающие и для VS, и для GNU, может выглядеть следующим образом: http://ideone.com/y39aaF

    Если же вы используете только ГНУшный компилятор, то можно сделать чуть проще: http://ideone.com/8PwAyg

    Использовал комменты с этой странички

»
11 лет назад, # |
Rev. 3   Проголосовать: нравится -19 Проголосовать: не нравится

Для любителей вывода с помощью оператора << можно сделать так:

class DebugOut
{
	template<class T>
	friend DebugOut& operator << (DebugOut &os, const T &val)
	{
		std::cerr << val << " ";
		return os;
	}
public:
	~DebugOut()
	{
		std::cerr << std::endl;
	}
};

class DebugOutProxy
{
public:
	DebugOutProxy()
		: mObj(new DebugOut()) {}
	template<class T>
	friend DebugOut &operator << (DebugOutProxy &os, const T &val)
	{
		return *os.mObj << val;
	}
private:
	std::shared_ptr<DebugOut> mObj;
};

DebugOutProxy dout()
{
	return DebugOutProxy();
}

Использование: dout() << "abc" << 123;

Не хватает тут разве что специализаций для возможности вывода контейнеров.