2009-03-24 2 views
33

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

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

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

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

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

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

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

Тогда, когда Assert пожары, то MessageBox бы показать что-то вроде:

Function = CMainFrame::OnCreate 

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

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

+0

Если у вас есть доступ к приложениям отладки John Robbins для Microsoft® .NET и Microsoft Windows® (http://www.amazon.com/Debugging-Applications-Microsoft®-Microsoft-Pro-Developer/dp/0735615365/ref = sr_1_16/175-5814253-7853112? Ie = UTF8 & s = electronics & qid = 1237928779 & sr = 8-16), вы должны обязательно посмотреть на утверждения из библиотеки BugSlayerUtil на прилагаемом компакт-диске. Они являются Windows-специфическими, но действительно конечными. – Paul

+0

Я посмотрел на http://www.koders.com/cpp/fid3653A5E08C30DB8B7551729FBED0BC3D51B19AD8.aspx, но он, похоже, не показывал имя функции. Поэтому я думаю, что моя версия более совершенна, чем его: D – demoncodemonkey

+0

Он показывает stacktrace. Stacktrace содержит имена функций, не так ли? – Paul

ответ

48

Ваш макрос может содержать макрос __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()" 
} 
+4

К сожалению, '__FUNCTION__' не является стандартным. '__func__' * является * стандартным, но это не строковый литерал; он действует как переменная, поэтому вы не можете легко ее конкатенировать. – ephemient

+0

правый. но, к счастью, OP использует MFC, и поэтому кросс-платформенность, вероятно, не проблема. –

+2

Я упомянул об этом, потому что ОП попросил мобильность. – ephemient

18

++ препроцессор 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 
+0

__FUNCTION__ не является частью стандартного языка C++ (для этого я не уменьшал yoou, но я думаю, что кто-то еще сделал) – 2009-03-24 20:24:09

+0

хороший момент, я думаю, что я что-то неправильно понял. в соответствии с http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html, __func__ является частью стандарта C99 (хотя не обязательно все его поддерживает). __FUNCTION__ не является, хотя, если большие compliers (MSDN/GCC) поддерживают его, он эффективно переносится. – YenTheFirst

+1

Обратите внимание, что __func__ также не является частью C++. – 2009-03-24 20:39:55

4

Вы можете использовать __FUNCTION__ macro, который во время компиляции будет расширена с именем функции.

Вот пример использования макроса 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

Арг на «...», но в остальном хороший ответ, спасибо. – demoncodemonkey

+0

Почему вы ненавидите на '...'? Это действительный код. –

+0

@demoncodemonkey: Тревор задает очень вопрос. Почему бы Эндрю не использовать здесь вариационную функцию? – unforgettableid

18

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

+1

Спасибо, я не использую Boost (пока), но, по крайней мере, я знаю, что есть замена __FUNCTION__, если мне когда-либо понадобится его порт. – demoncodemonkey

+0

Заголовок для {{BOOST_CURRENT_FUNCTION}} является автономным. Вы можете просто загрузить его и поместить в свою кодовую базу без каких-либо дополнительных ссылок из Boost. –

7

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

EDIT: по-видимому __func__ является частью стандарта? не знал этого. К сожалению, он дает имя функции, а не параметры. Мне нравится gcc __PRETTY_FUNC__, но он не переносится на другие компиляторы.

GCC также поддерживает __FUNCTION__.

Смежные вопросы