Использование __FILE__, __LINE__ и __FUNCTION__ в C++

Предполагая, что ваш компилятор C++ поддерживает их, есть ли какая-либо особая причина не использовать __FILE__, __LINE__ и __FUNCTION__ для целей ведения журнала и отладки?

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

По сути, могу ли я доверять __FILE__, __LINE__ и __FUNCTION__ всегда поступать правильно?


person Runcible    schedule 27.02.2009    source источник
comment
LINE должен действовать правильно. Я широко использовал его и его когорты, включая PRETTY_FUNCTION. ... Но ... ну, я сейчас смотрю код, где лежит LINE. Вероятно, потому что он находится в блоке catch для обработки исключений try/catch.   -  person Krazy Glew    schedule 11.10.2012
comment
актуально: справочник gcc по предопределенным макросам   -  person Alexander Malakhov    schedule 02.11.2012


Ответы (6)


__FUNCTION__ не является стандартным, __func__ существует в C99/C++11. Остальные (__LINE__ и __FILE__) в порядке.

Он всегда будет сообщать правильный файл и строку (и функцию, если вы решите использовать __FUNCTION__/__func__). Оптимизация не является фактором, поскольку это расширение макроса времени компиляции; это никогда никак не повлияет на производительность.

person Evan Teran    schedule 27.02.2009
comment
__func__ - это своего рода проблема в C++. C99 не говорит ни слова об аргументах по умолчанию и т. д., случаях, когда не так очевидно, как __func__ должен вести себя в C++. - person wilhelmtell; 29.01.2012
comment
@thr: в то время как вы делаете хорошее замечание. Мне было совершенно ясно, что __func__ существует в c99, а не в C++. Несмотря на это, я думаю, что разумная реализация __func__ в С++ просто приведет к искаженному имени. Поскольку я не создаю компиляторы, это не совсем моя задача. - person Evan Teran; 05.04.2012
comment
Какие компиляторы вообще не поддерживают __FUNCTION__? Какие компиляторы, кроме недавнего gcc, рассматривают это как переменную, а не макрос? - person basin; 18.01.2013
comment
__func__ теперь находится в стандарте C++11. - person V-X; 25.09.2013

В редких случаях может быть полезно изменить строку, заданную __LINE__, на что-то другое. Я видел, как GNU configure делает это для некоторых тестов, чтобы сообщить соответствующие номера строк после того, как он вставил какое-то вуду между строками, которых нет в исходных исходных файлах. Например:

#line 100

Следующие строки будут начинаться с __LINE__ 100. При желании вы можете добавить новое имя файла

#line 100 "file.c"

Это редко полезно. Но если это необходимо, альтернативы я не знаю. На самом деле, вместо строки можно использовать и макрос, который должен привести к любой из двух вышеперечисленных форм. Используя библиотеку препроцессора boost, вы можете увеличить текущую строку на 50:

#line BOOST_PP_ADD(__LINE__, 50)

Я подумал, что полезно упомянуть об этом, поскольку вы спрашивали об использовании __LINE__ и __FILE__. От С++ никогда не бывает достаточно сюрпризов :)

Редактировать: @Jonathan Leffler предлагает еще несколько хороших вариантов использования в комментариях:

Игра с #line очень полезна для препроцессоров, которые хотят, чтобы сообщения об ошибках в пользовательском коде C соответствовали исходному файлу пользователя. Это делают препроцессоры Yacc, Lex и (что мне ближе) препроцессоры ESQL/C.

person Johannes Schaub - litb    schedule 27.02.2009

К вашему сведению: g++ предлагает нестандартный макрос __PRETTY_FUNCTION__. До сих пор я не знал о C99 __func__ (спасибо, Эван!). Я думаю, что по-прежнему предпочитаю __PRETTY_FUNCTION__, когда он доступен для дополнительной области видимости класса.

PS:

static string  getScopedClassMethod( string thePrettyFunction )
{
  size_t index = thePrettyFunction . find( "(" );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( index );

  index = thePrettyFunction . rfind( " " );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( 0, index + 1 );

  return thePrettyFunction;   /* The scoped class name. */
}
person Mr.Ree    schedule 28.02.2009
comment
Приятно узнать о __PRETTY_FUNCTION__. Очень полезно! - person Zheng Qu; 12.12.2017

С++ 20 std::source_location

В C++ наконец-то добавлен параметр, не связанный с макросами, и он, вероятно, станет доминирующим в какой-то момент в будущем, когда C++20 станет широко распространенным:

В документации говорится:

constexpr const char* имя_функции() const noexcept;

6 Возвращает: Если этот объект представляет позицию в теле функции, возвращает определяемую реализацией NTBS, которая должна соответствовать имени функции. В противном случае возвращает пустую строку.

где NTBS означает «строка байтов с нулевым завершением».

Я попробую, когда появится поддержка GCC, GCC 9.1.0 с g++-9 -std=c++2a все еще не поддерживает его.

https://en.cppreference.com/w/cpp/utility/source_location использование утверждений будет выглядеть так:

#include <iostream>
#include <string_view>
#include <source_location>

void log(std::string_view message,
         const std::source_location& location std::source_location::current()
) {
    std::cout << "info:"
              << location.file_name() << ":"
              << location.line() << ":"
              << location.function_name() << " "
              << message << '\n';
}

int main() {
    log("Hello world!");
}

Возможный вывод:

info:main.cpp:16:main Hello world!

__PRETTY_FUNCTION__ против __FUNCTION__ против __func__ против std::source_location::function_name

Отвечено по адресу: В чем разница между __PRETTY_FUNCTION__, __FUNCTION__, __func__ ?

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 15.01.2020
comment
В текущем gcc-9 есть <experimental/source_location>. - person 陈浩南; 24.05.2020

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

person Craig S    schedule 27.02.2009
comment
Я мог бы записать эту информацию в консоль или еще лучше: записать ее в файл, чтобы, если что-то пойдет не так, вы могли попросить клиента отправить ее вам... - person Christoph; 28.02.2009

Я использую их все время. Единственное, о чем я беспокоюсь, это выдача IP в лог-файлах. Если ваши имена функций действительно хороши, возможно, вы упрощаете раскрытие коммерческой тайны. Это похоже на отправку с отладочными символами, только сложнее найти что-то. В 99,999% случаев ничего страшного из этого не выйдет.

person JeffCharter    schedule 05.02.2015
comment
Хороший вопрос, чтобы поднять. Эту информацию легко извлечь с помощью утилиты strings для извлечения всех строковых данных из исполняемого файла. Можно извлечь даже сжатые исполняемые файлы. Будьте очень внимательны к тому, что вы отправляете на сайт клиента. Часто конкуренты могут заполучить ваши исполняемые файлы, даже если они не должны этого делать. - person Marty; 14.12.2015
comment
Это возможно с constexpr, который использует таблицу поиска или побитовое XOR и т. д., чтобы запутать любые строковые литералы в вашем двоичном файле. Есть разные примеры, если поискать. Конечно, запутывание — это просто запутывание, а не безопасность, но если вы не хотите, чтобы ваши файлы и функции были очевидны в двоичном коде, это вариант. - person idij; 24.07.2020