Подтвердить что ты не робот

Доступ к значениям переменных внутри макроса

Некоторое время назад я сделал этот красивый макрос assert для программ c и С++

#define ASSERT(truthy, message) \
     if (!(truthy)) \
     {\
         cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy << endl;\
     }

Абонент Scatter ASSERT выполняет весь код, и он будет предупреждать вас, когда значение truthy не является правдивым! Очень удобно во время разработки, чтобы напомнить вам о возможных ошибках.

ех

ASSERT(filesFound > 0, "Couldn't find any files, check your path!");

Когда filesFound равен 0, макрос распечатает

Не удалось найти файлы, проверить свой путь! в строке 27 в файле openFiles.c. Проверьте, были ли файлыFound > 0

Теперь то, что я хочу, чтобы оно печаталось, чтобы дать мне еще более актуальную информацию, является значение любых переменных, переданных в параметр truthy. Как этот

Не удалось найти файлы, проверить свой путь! в строке 27 в файле openFiles.c. Проверка была filesFound > 0, filesFound - 0

Похоже, что это выглядит как lisp -полюсная территория, есть ли какая-либо черная магия c предварительной обработки, которую я могу использовать для оценки переменных и функций по их значениям без оценки оператора truthy?

Я предполагаю, что разочарован.

4b9b3361

Ответ 1

Альтернативным решением, которое я всегда использовал, является поддержка varargs в макросе, а затем принуждение пользователя-пользователя указывать соответствующие сообщения/переменные - это немного дополнительная работа каждый раз, но на плюсе вы можете получите именно то форматирование, которое вы хотите, и включите информацию, недоступную в "правдивом" бите, например:

#define ASSERT(truthy, message, ...) \
if (!(truthy)) \
{\
    MyAssertHandler(__LINE__, __FILE__, #truthy, message, ##__VA_ARGS__);
}

Тогда вы обработчик - это просто стандартная функция var-arg, которая может использовать, например. vsnprintf для генерации сообщения и вывода его, например. от верхней части головы:

void MyAssertHandler(int line, const char* file, const char* expressionStr, const char* format, ...)
{
    // Note: You probably want to use vsnprintf instead to first generate
    //       the message and then add extra info (line, filename, etc.) to
    //       the actual output 
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);

    // Log to bug database, DebugBreak() if a debugger is attached, etc.
}

использование:

ASSERT(IsBlah(), "BlahBlah: x = %.2f, name = %s", GetX(), GetName());

Ответ 2

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

#define ASSERT_PARAM(truthy, message, param) \
     if (!(truthy)) \
     {\
         cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy  << ", value was " << param << endl;\
     }

Вы бы использовали его таким образом:

ASSERT_PARAM(filesFound > 0, "Couldn't find any files, check your path!", filesFound);

получаю:

Couldn't find any files, check your path! on line 27 in file openFiles.c. Check was filesFound > 0, value was 0

Ответ 3

То, что вы пытаетесь сделать, звучит очень сложно. Я боюсь, в С++ это невозможно.

Технически то, что вы оцениваете, является выражением bool, поэтому вы можете передать его парсеру всякий раз, когда утверждение терпит неудачу. Затем синтаксический анализатор построит дерево выражений, получит листья (элементы выражения) и вернет их. Возвращенные значения затем должны быть распечатаны. Для этого вам понадобится поддержка для отражения, которая на самом деле не поддерживается в С++ AFAIK.

Ответ 4

Возможно, это не решение мечты, но вы можете передать целые утверждения макросу.

#define ASSERT(trusty, action) if (!trusty) { action }

ASSERT(trusty, cout << a << b;)
ASSERT(trusty, printf("%d, %f\n", a, b);)

Ответ 5

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

Затем печать может быть возобновлена ​​с использованием функции вариационного шаблона

Ответ 6

Возможно, вы можете пойти на компромисс и разрешить только 2 переменные и 1 оператор в выражении утверждения? Если это так, вы можете сделать специальное решение, подобное этому:

#include <iostream>
#include <string>

#define STRINGIFY(x) #x

#define BIN_ASSERT(obj1, op, obj2, msg)                                 \
  if(!(obj1 op obj2))                                                   \
  {                                                                     \
    std::cout << msg << " on line " << __LINE__                         \
         << " in file " << __FILE__                                     \
         << "." << std::endl                                            \
         << "Check was "                                                \
         << STRINGIFY(obj1) STRINGIFY(op) STRINGIFY(obj2)               \
         << "." << std::endl                                            \
         << "Operator " << #obj1 << ": " << obj1                        \
         << "." << std::endl                                            \
         << "Operator " << #obj2 << ": " << obj2                        \
         << "." << std::endl;                                           \
  }


int main (void)
{
  int x = 2;
  int y = 3;
  std::string s1 = "hello";
  std::string s2 = "world";

  BIN_ASSERT(1, +, -1, "Value zero"); std::cout << std::endl;
  BIN_ASSERT(x, ==, y, "Numbers not equal"); std::cout << std::endl;
  BIN_ASSERT(s1, ==, s2, "Strings not equal"); std::cout << std::endl;
}

Вывод:

Value zero on line 30 in file test.c.
Check was 1+-1.
Operator 1: 1.
Operator -1: -1.

Numbers not equal on line 31 in file test.c.
Check was x==y.
Operator x: 2.
Operator y: 3.

Strings not equal on line 32 in file test.c.
Check was s1==s2.
Operator s1: hello.
Operator s2: world.

Ответ 7

Интересно, действительно ли полезно использовать макрос сообщения. Неудачное утверждение - это сообщение разработчику о том, что в коде есть ошибка, которая вызвала исключительное поведение или поставила программу в недопустимое состояние. Пользователь имеет меньше общего с ним (если у них даже есть доступ к исходному коду).

В приведенном ниже коде описывается макрос ASSERT, который принимает логическое выражение, оценивает его и печатает информационное сообщение. Сообщение содержит значение, которое вы попросили проверить после отказа от утверждения.

Макрос, как и стандартный макрос assert()<cassert>), вызывается abort() (от <cstdlib>), чтобы вызвать ненормальное завершение программы. Это то, что вы хотите, потому что программа ввела состояние, в котором оно не знало, что больше делать.

Я использую std::printf() здесь для краткости. Вы делаете все, что хотите.

#include <cstdlib>
#include <cstdio>

#define ASSERT(value, inspect)                                                 \
    if (!(value)) {                                                            \
        std::printf("ASSERTION FAILED: '%s', %s is %d: %[email protected]%s:%d\n", #value,    \
                    #inspect, inspect, __func__, __FILE__, __LINE__);          \
        abort();                                                               \
    }

int foo() { return 42; }

int main()
{
    // ...
    ASSERT(foo() - 40 == 1, foo());
    //...
}

Запуск программы:

$ ./a.out
ASSERTION FAILED: 'foo() - 40 == 1', foo() is 42: [email protected]:16
Abort

Невозможно выполнить именно то, что вы просите, не добавляя в макрос больше параметров. В какой-то момент вам придется остановиться и понять, что вы тратите время на создание текстовой строки, которую вы не хотите видеть.

Ответ 8

Вам нужно построить выражение 'grabber'/builder.

Макрос будет выглядеть примерно так:

#define ASSERT_PARAM(truthy, message, param) \
 if (!(truthy)) \
 {\
     Grabber g;
     g << #truthy; // grab expression as string
     g % truthy;  // grab expression and values
     cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy  << ", value was " << param << endl;\
     cout << g; \
 }

Что делает Grabber?

Это куча сумасшедшего С++, который создает выражение. Это перегрузило бы каждого оператора, чтобы "захватить" параметры оператора. Каждый оператор возвращает ссылку на граббер, поэтому он может захватить следующий оператор. то есть

Grabber g;
g % filesFound > 0;

Так как% (и * и /) имеют высокий приоритет, вышеприведенные разборки похожи на:

((g % filesFound) > 0)

Если template<typename T> Grabber::operator%(T const & val) просто записывает (или печатает) значение, переданное в (т.е. filesFound) и, что важно, возвращает себя (g), так что оно становится частью следующего выражения: ie оно становится g > 0. Причинение template<typename T> Grabber::operator>(T const & val), и > 0 для записи.

Тогда cout << g может извергнуть все захваченное.

Как упоминалось выше "Возможно - библиотека Catch делает это, но ее адски трудно".

P.S. вы должны обернуть свой макрос в do... while 0, как это:

#define ASSERT_PARAM(truthy, message, param) \
 do \
 { \
   if (!(truthy)) \
   {\
     cout << message << " on line " << __LINE__ << " in file " << __FILE__ << ". Check was " << #truthy  << ", value was " << param << endl;\
     cout << g; \
   } \
 } while (0)

Что вы в настоящее время имеете в виду, что это действительный код:

ASSERT(foo != 0)
else
{
}

И это НЕВОЗМОЖНЫЙ код:

if (foo != nullptr)
   ASSERT(foo->bar != nullptr);
else
   x = 10;

Ответ 9

Удивительно, но раньше я решил аналогичную проблему, но я не уверен, помогло бы вам в этом случае.

Оригинальное решение было предложено Андреем Александреску в статье "Усиление утверждений" и без сомнения, опираясь на некоторые макро-трюки.

Этот удивительный объект можно использовать как:

string s1, s2;
...
SMART_ASSERT(s1.empty() && s2.empty())(s1)(s2);

И если что-то пойдет не так, сообщение будет отображаться

Assertion failed in matrix.cpp: 879412:
Expression: 's1.empty() && s2.empty()'
Values: s1 = "Wake up, Neo"
        s2 = "It time to reload."

Следует отметить, что SMART_ASSERT может теоретически фиксировать бесконечные переменные.

Сведения о реализации см. в статье.