Как найти имя текущей функции во время выполнения?
Вопрос
После многих лет использования большого уродливого макроса MFC ASSERT я наконец решил отказаться от него и создать совершенный макрос ASSERT.
Меня устраивает получение номера файла и строки и даже выражения, которое не удалось.Я могу отобразить окно сообщения с ними и кнопки «Прервать/Повторить/Отменить».
И когда я нажимаю «Повторить», отладчик VS переходит к строке, содержащей вызов ASSERT (в отличие от дизассемблирования где-то, как некоторые другие функции ASSERT).Так что все вполне работает.
Но что было бы действительно здорово, так это отобразить имя функции, которая не удалась.
Тогда я смогу решить, следует ли его отлаживать, не пытаясь угадать, в какой функции он находится, по имени файла.
напримересли у меня есть следующая функция:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
ASSERT(lpCreateStruct->cx > 0);
...
}
Затем, когда сработает ASSERT, в окне сообщения появится что-то вроде:
Function = CMainFrame::OnCreate
Итак, каков самый простой способ узнать текущее имя функции во время выполнения?
Он не должен использовать MFC или платформу .NET, хотя я использую и то, и другое.
Он должен быть максимально портативным.
Решение
Ваш макрос может содержать __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()"
}
Другие советы
Макрос препроцессора C++ __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
Стандартного решения не существует.Однако, BOOST_CURRENT_FUNCTION
портативен для всех практических целей.Заголовок не зависит ни от одного из других заголовков Boost, поэтому его можно использовать автономно, если накладные расходы всей библиотеки неприемлемы.
__FUNCTION__
или __FUNC__
или __PRETTY_FUNCTION__
http://msdn.microsoft.com/en-us/library/b0084kay(VS.80).aspx http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html
В GCC вы можете использовать __PRETTY_FUNCTION__
макрос.
У Microsoft также есть аналог __func__
макрос, хотя у меня его нет, чтобы попробовать.
напримериспользовать __PRETTY_FUNCTION__
поместив что-то подобное в начало ваших функций, вы получите полную трассировку
void foo(char* bar){
cout << __PRETTY_FUNCTION__ << std::endl
}
который выведет
void foo(char* bar)
У вас также есть __FILE__
и __LINE__
макросы, доступные во всех стандартных компиляторах c/c++, если вы хотите вывести еще больше информации.
На практике у меня есть специальный класс отладки, который я использую вместо cout.Определив соответствующие переменные среды, я могу получить полную трассировку программы.Вы могли бы сделать что-то подобное.Эти макросы невероятно удобны, и очень здорово иметь возможность включать такую выборочную отладку в полевых условиях.
РЕДАКТИРОВАТЬ:видимо __func__
является частью стандарта?не знал этого.К сожалению, он дает только имя функции, а не параметры.мне нравятся gcc __PRETTY_FUNC__
но он не переносится на другие компиляторы.
GCC также поддерживает __FUNCTION__
.
Вы можете использовать __FUNCTION__
макрос который во время компиляции будет расширен до имени функции.
Вот пример того, как использовать его в макросе утверждения.
#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);
}
вы можете легко использовать функция.он вернет вам текущее имя функции во время выполнения, которое вызвало исключение.
Применение:
cout << __func__ << ": " << e.what();