Подтвердить что ты не робот

Как найти имя текущей функции во время выполнения?

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

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

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

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

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

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

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

Затем, когда срабатывает ASSERT, в окне сообщений появится следующее:

Function = CMainFrame::OnCreate

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

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

4b9b3361

Ответ 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, поэтому их можно использовать автономно, если накладные расходы всей библиотеки неприемлемы.

Ответ 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);
}