Как найти имя текущей функции во время выполнения? - PullRequest
35 голосов
/ 24 марта 2009

После многих лет использования большого уродливого макроса MFC ASSERT я наконец решил отказаться от него и создать идеальный макрос ASSERT.

Я в порядке с получением файла и номера строки, и даже выражения, которое не удалось. Я могу отобразить окно сообщений с этими кнопками и Отменить / Повторить / Отменить.

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

Но что было бы действительно круто, так это отобразить название функции, которая потерпела неудачу .

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

например. если у меня есть следующая функция:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   ASSERT(lpCreateStruct->cx > 0);
   ...
}

Тогда при срабатывании ASSERT в окне сообщения будет что-то вроде:

Function = CMainFrame::OnCreate

Итак, как проще всего узнать текущее имя функции во время выполнения?

Он не должен использовать MFC или .NET Framework, хотя я использую оба из них.
Он должен быть максимально переносимым.

Ответы [ 7 ]

51 голосов
/ 24 марта 2009

Ваш макрос может содержать макрос __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()"
}
23 голосов
/ 24 марта 2009

Макрос препроцессора 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
18 голосов
/ 25 марта 2009

Там нет стандартного решения. Однако BOOST_CURRENT_FUNCTION является переносимым для всех практических целей. Заголовок не зависит от других заголовков Boost, поэтому может использоваться автономно, если накладные расходы всей библиотеки недопустимы.

10 голосов
/ 24 марта 2009
8 голосов
/ 24 июля 2009

В 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__.

5 голосов
/ 24 марта 2009

Вы можете использовать макрос __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);
}
0 голосов
/ 25 марта 2019

вы можете легко использовать func . он вернет вам текущее имя функции во время выполнения, которое вызвало исключение.

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

cout << __func__ << ": " << e.what();
...