Идентификатор вызова функции C ++ - PullRequest
5 голосов
/ 10 июля 2009

Рассмотрим следующий код:

void Foo() {
  ......
  LOG_ERROR("I'm error 1")   // call 1
  .....
  LOG_ERROR("I'm error 2")  // call 2
  .....

}

LOG_ERROR() - это макрос. LOG_ERROR() должен напечатать строку, идентифицирующую его в коде, хотя предполагается, что код может измениться, но A::Foo() останется без изменений. Идентификатор должен быть сохранен, пока код изменения.

Эту проблему можно решить, добавив код ошибки в качестве аргумента в LOG_ERROR(), но мы хотим снять с программиста бремя управления ошибками коды.

Использование __LINE__ не является ответом, поскольку Foo() может перейти от сборки к строить.

Поэтому я подумал об идентификации LOG_ERROR() относительно начала Foo()

  • а. Идентифицировать по имени файла (__FILE__) + имя функции (__FUNCTION__) + номер строки LOG_ERROR() относительно Foo() start.
  • б. Идентифицировать по имени файла (__FILE__) + имя функции (__FUNCTION__) + LOG_ERROR() номер звонка в Foo().

Решение должно работать как минимум с VC ++ 2008 и g ++ 4.1.1.

Одно из предложенных решений ( текст ссылки ):

#define ENABLE_LOG_ERROR static const int LOG_ERROR_start_line = __LINE__
#define LOG_ERROR(s) cerr << "error #" << (__LINE__ - LOG_ERROR_start_line) \
    << " in " << __func__ << ": " << s << endl

void Foo() {
     ENABLE_LOG_ERROR;
     //...
     LOG_ERROR("error 1");
     int i;
     LOG_ERROR("error 2");
} 

Это заставит пользователя писать ENABLE_LOG_ERROR в начале каждой функции, содержащей LOG_ERROR() и таких функций много.

Есть ли другой способ выполнить задачу?

Ответы [ 3 ]

1 голос
/ 10 июля 2009

Это решение нестандартное , но и MSVC, и GCC поддерживают __COUNTER__, которое увеличивается при каждом вызове.

#define LOG_ERROR(s) cerr << "error #" << (__COUNTER__) << " in " \
<< __func__ << ": " << s << endl

Обратите внимание, что __COUNTER__ будет сбрасываться в каждом модуле компиляции и ТОЛЬКО в каждом модуле компиляции. Поэтому, если Foo() имеет 7 LOG_ERROR() макросов, в более поздней функции Bar() значение __COUNTER__ будет равно 7 для первого использования LOG_ERROR().

1 голос
/ 11 июля 2009

Хотя вопрос касается способов генерирования уникальных идентификаторов строк внутри функции для целей ведения журнала, я собираюсь сделать шаг назад и посмотреть на реальную проблему, которая должна быть решена: как генерировать выходные данные журнала, которые могут легко идентифицировать строка исходного кода, не обременяя автора кода.

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

Если эти предположения верны, то решение состоит в том, чтобы ваша программа записала свою текущую версию в файл журнала. Тогда каждая отдельная запись в журнале может просто записать номер строки через __LINE__.

Таким образом, когда кому-то нужно использовать журнал: он может посмотреть номер версии в журнале, взять соответствующий источник из репозитория управления исходным кодом и использовать номера строк из журнала, чтобы перейти к нужным исходным строкам. , Это немного увеличивает нагрузку на человека, использующего вывод журнала. Однако, если регистрируемый код зависит или зависит от другого кода, который может меняться от версии к версии, то в любом случае может потребоваться историческое состояние исходного кода.

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


Что касается реализации, вы можете либо зарегистрировать версию программы при запуске программы, либо сделать так, чтобы макрос регистрации включал ее в каждую запись.

Если версия программы обычно хранится где-то, что не легко доступно в обычном исходном коде, то вы можете создать шаг предварительной сборки, который извлечет версию и запишет ее в простой файл version.h в виде строки #define или const , Тогда регистрационный код или макрос может автоматически использовать его, чтобы всегда выводить текущую версию программы.

0 голосов
/ 10 июля 2009

Изменяя идею стека, используйте отображение std::map из std::string в счетчик и найдите имя функции.

std::map<std::string, int> LOG_ERROR_count_map;
#define LOG_ERROR(s) {\
  int count = ++LOG_ERROR_count_map[ __func__ ];\
  std::cout << count << " in " __func__ ": " s << std::endl;\
}

Это означает, что вам не нужен ENABLE_LOG_ERROR, но за счет просмотра карты для каждого журнала. (Это компромисс между простотой использования и временем.)

...