Как найти имя текущей функции во время выполнения?
После нескольких лет использования большого уродливого макроса MFC ASSERT я, наконец, решил сорвать его и создать конечный макрос ASSERT.
Я в порядке с получением файла и номера строки, и даже выражение, которое не удалось. Я могу отображать в них окно сообщений и кнопки "Прервать/Повторить/Отменить".
И когда я нажимаю Retry, отладчик VS переходит к строке, содержащей вызов ASSERT (в отличие от разборки где-то, как некоторые другие функции ASSERT). Так что все это в значительной степени работает.
Но что было бы действительно круто, было бы отображать имя неудавшейся функции.
Затем я могу решить, отлаживать ли его, не пытаясь угадать, какую функцию он использует в имени файла.
например. если у меня есть следующая функция:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
ASSERT(lpCreateStruct->cx > 0);
...
}
Затем, когда срабатывает ASSERT, в окне сообщений появится следующее:
Function = CMainFrame::OnCreate
Итак, какой самый простой способ узнать текущее имя функции во время выполнения?
Он не должен использовать MFC или платформу .NET, хотя я использую оба из них.
Он должен быть как можно более переносимым.
Ответы
Ответ 1
Ваш макрос может содержать макрос __FUNCTION__
.
Не ошибитесь, имя функции будет вставлено в расширенный код в время компиляции, но это будет правильное имя функции для каждого вызова вашего макроса. Так что "похоже", это происходит во время выполнения;)
например.
#define THROW_IF(val) if (val) throw "error in " __FUNCTION__
int foo()
{
int a = 0;
THROW_IF(a > 0); // will throw "error in foo()"
}
Ответ 2
Макрос препроцессора С++ __FUNCTION__
дает имя функции.
Обратите внимание, что если вы используете это, оно не получает имя файла, номер строки или имя функции во время выполнения. Макросы расширены препроцессором и скомпилированы.
Макрос __FUNCTION__
, такой как __LINE__
и __FILE__
, является частью стандартного языка и переносится.
Пример программы:
#include <iostream>
#using namespace std;
void function1()
{
cout << "my function name is: " << __FUNCTION__ << "\n";
}
int main()
{
cout << "my function name is: " << __FUNCTION__ << "\n";
function1();
return 0;
}
выход:
my function name is: main
my function name is: function1
Ответ 3
Нет стандартного решения. Однако BOOST_CURRENT_FUNCTION
переносится для всех практических целей. Заголовок не зависит от каких-либо других заголовков Boost, поэтому их можно использовать автономно, если накладные расходы всей библиотеки неприемлемы.
Ответ 4
__FUNCTION__
или __FUNC__
или __PRETTY_FUNCTION__
http://msdn.microsoft.com/en-us/library/b0084kay(VS.80).aspx
http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
Ответ 5
В GCC вы можете использовать макрос __PRETTY_FUNCTION__
.
Microsoft также имеет эквивалентный макрос __func__
, хотя у меня нет этого, чтобы попробовать.
например. использовать __PRETTY_FUNCTION__
, помещая что-то вроде этого в начале ваших функций, и вы получите полную трассировку
void foo(char* bar){
cout << __PRETTY_FUNCTION__ << std::endl
}
который выведет
void foo(char* bar)
У вас также есть макросы __FILE__
и __LINE__
, доступные для всех стандартных компиляторов c/С++, если вы хотите вывести еще больше информации.
На практике у меня есть специальный класс отладки, который я использую вместо cout. Определив соответствующие переменные среды, я могу получить полную трассировку программы. Вы могли бы сделать что-то подобное. Эти макросы невероятно удобны, и действительно здорово, что можно включить выборочную отладку в поле.
EDIT: видимо __func__
является частью стандарта? не знал этого. К сожалению, он дает имя функции, а не параметры. Мне нравится gcc __PRETTY_FUNC__
, но он не переносится на другие компиляторы.
GCC также поддерживает __FUNCTION__
.
Ответ 6
Вы можете использовать макрос __FUNCTION__
, который во время компиляции будет расширен до имени функции.
Вот пример использования макроса assert.
#define ASSERT(cond) \
do { if (!(cond)) \
MessageBoxFunction("Failed: %s in Function %s", #cond, __FUNCTION__);\
} while(0)
void MessageBoxFunction(const char* const msg, ...)
{
char szAssertMsg[2048];
// format args
va_list vargs;
va_start(vargs, msg);
vsprintf(szAssertMsg, msg, vargs);
va_end(vargs);
::MessageBoxA(NULL, szAssertMsg, "Failed Assertion", MB_ICONERROR | MB_OK);
}