2013-08-18 1 views
14

Я использую gcc для компиляции кода C99. Я хочу написать макрос, который вернет строку, содержащую имя функции и номер строки.Обработка __func__ как строкового литерала вместо предопределенного идентификатора

Это то, что у меня есть:

#define INFO_MSG __FILE__ ":"__func__"()" 

Однако, когда я скомпилировать код, который пытается использовать эту строку, например:

char buff[256] = {'\0'} 
sprintf(buff, "Something bad happened here: %s, at line: %d", INFO_MSG, __LINE__); 
printf("INFO: %s\n", buff); 

Я получаю следующее сообщение об ошибке:

error: expected ‘)’ before ‘__func__’ 

Я отследил проблему до макроса. как когда я удаляю __func__ из макроса, код компилируется правильно.

Как исправить макрос, чтобы я мог включить предопределенный макрос в свою строку?

+1

Используйте переключатель расширителя компилятора, чтобы узнать, что выплевывает. – leppie

+1

Я не верю, что '__func__' - это макрос. Может быть, вы могли бы поместить 'INFO_MSG' в строку формата вместо этого? 'sprintf (buff,« Что-то плохое произошло здесь:% s:% s(), в строке:% d ", __FILE__, __func__, __LINE __);' – tangrs

+0

@leppie: вы можете уточнить? - Что вы подразумеваете под расширителем ?. Кстати, я использую gcc. –

ответ

17

Судя по вашим комментариям, цель состоит в том, чтобы иметь макрос, который сочетает в себе имя файла и имя функции (и, возможно, номер строки) в одну строку, которая может быть передана в качестве аргумента функции, такие как printf() или strcpy() или syslog() ,

К сожалению, я не думаю, что это возможно.

Стандарт C11 говорит:

ISO/IEC 9899: 2011 §6.4.2.2 предопределенные идентификаторы

¶1 Идентификатор __func__ должен быть неявно объявляется переводчиком, как если сразу после открывающая скобка каждой функции, декларация

static const char __func__[] = "function-name"; 

появилось, где function-name - имя лексически-охватывающая функция.

Поэтому __func__ не макро, в отличие от __FILE__ или __LINE__.

Связанный с этим вопрос What's the difference between __PRETTY_FUNCTION__, __FUNCTION__, __func__? охватывает некоторые альтернативные имена. Это GCC-специфические расширения, а не стандартные имена. Кроме того, в документации GCC 4.8.1 говорится:

Эти идентификаторы не являются макросами препроцессора. В GCC 3.3 и ранее, только в C, __FUNCTION__ и __PRETTY_FUNCTION__ рассматривались как строковые литералы; они могут быть использованы для инициализации массивов символов, и они могут быть объединены с другими строковыми литералами. GCC 3.4 и выше относятся к ним как к переменным, например __func__. В C++ __FUNCTION__ и __PRETTY_FUNCTION__ всегда были переменными.

Есть веские причины, по которым они не могут быть конструкциями препроцессора. Препроцессор не знает, что такое функция, и является ли текст, который он обрабатывает, входит в область действия функции или то, что является именем закрывающей функции. Это простой текстовый процессор, а не компилятор. Понятно, что можно было бы создать такое понимание в препроцессоре (исключительно для поддержки этой функции), но стандарт не требуется, и стандарт не должен требоваться.

К сожалению, хотя, я думаю, что это означает, что попытки объединить __func__ (любым правописанием) с __FILE__ и __LINE__ в одном макросе для создания одной строки буквальной обречены.

Очевидно, что вы можете создать имя файла и номер строки в виде строки с помощью стандартного механизма макросъемки два этапа:

#define STR(x) #x 
#define STRINGIFY(x) STR(x) 

#define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__) 

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

Есть аргументы, что имя файла и номер строки достаточны, чтобы определить, где проблема; имя функции едва ли необходимо. Он более косметический, чем функциональный, и немного помогает программистам, но не другим пользователям.

+0

ОК, я думаю, что это нужно будет сделать тогда - по крайней мере, имя файла и номер строки должны помочь в отладочных сообщениях. –

+0

BTW, +1 для подробного объяснения и работы. –

+1

Существует одна страшная альтернатива, которая не является потокобезопасной, и которая использует копирование во время выполнения: 'extern const char * file_line_func (const char * file, int line, const char * func);' который вызывается из макроса и форматирует информацию в статическая строка (следовательно, она не является потокобезопасной) и возвращает указатель на эту строку. Работает нормально в вероятных контекстах использования (отчеты об ошибках), но скромно дорого. Вы можете немного улучшить, передав результат макроса FILE_LINE в качестве единственного аргумента вместо аргументов 'file' и' line'. Но это все еще некрасиво. Очень страшный. –

1

это синтаксическая ошибка. Я стараюсь прийти с макросъемки спецификации, но я не найти эффективный способ, так что, может быть, вы можете попробовать это:

#define INFO_MSG __FILE__ , __FUNCTION__ 

int main() 
{ 
    char buff[256] = {'\0'}; 
    sprintf(buff, "Something bad happened here: %s : %s(), at line: %d", INFO_MSG, __LINE__); 
    printf("INFO: %s\n", buff); 
} 
3

После быстрого эксперимента я обнаружил, что вы не можете использовать __func__ с stringification. Это не имело бы смысла, если бы вы могли это сделать, поскольку это означало бы, что значение будет везде, где определяется макрос, а не где он применяется.

Характер __func__, как указано в комментариях к вопросу, описан в this answer.

Стрингирование выполняется в процессорное время, и из-за этого __func__ недоступен, поскольку он по существу является локальной строкой функции, которая определена позже в процессе компиляции.

Однако вы можете использовать __func__ в макросе до тех пор, пока вы не используете строение на нем. Я думаю, что следующий выполняет то, что вы после:

#include <stdio.h> 

#define INFO_MSG "Something bad happened here: %s : %s(), at line: %d", \ 
       __FILE__, __func__, __LINE__ 

int main() 
{ 
    char buff[256] = {'\0'}; 
    sprintf(buff, INFO_MSG); 
    printf("INFO: %s\n", buff); 
    return 0; 
} 

Обратите внимание, что нет никаких особых причин, в этом вопросе, как представлено, чтобы использовать буфер строки. Следующий main функция будет достичь того же эффекта без возможности переполнения буфера:

int main() 
{ 
    printf("INFO: "); 
    printf(INFO_MSG); 
    printf("\n"); 
    return 0; 
} 

Лично я бы завернуть весь процесс в макро так:

#include <stdio.h> 

#define INFO_MSG(msg) printf("%s: %s : %s(), at line: %d\n", \ 
         msg, __FILE__, __func__, __LINE__) 

int main() 
{ 
    INFO_MSG("Something bad happened"); 
    return 0; 
} 
+0

Вы можете подстроить '__LINE__' с помощью обычного двухступенчатый трюк: '#define STR (x) # x',' #define STRINGIFY (x) STR (x) ', а затем' STRINGIFY (__ LINE __) '. Вы не можете ничего сделать с '__func__', который является предопределенным идентификатором (переменной), а не константной строкой. –

+0

Спасибо. Я исправил свой ответ. –

+0

Хм, это не идеально. Я использовал этот макрос для динамического генерации имени функции в виде строки (с более ранними версиями gcc). Мне придется переписать много кода только из-за этого, я буду исследовать, могу ли я избежать этого. BTW, мне нужна строка, поскольку я не всегда пишу на stdout (т. Е. Консоль), я использовал только printf для краткости/ясности. –

3

замечание, что " __func__ не является функцией, поэтому ее нельзя назвать, на самом деле это предопределенный идентификатор, указывающий на строку, которая является именем функции, и действительна только в пределах функции. "- Джонатан.

Ниже то, что вы ищете:

#define TO_STR_A(A) #A 
#define TO_STR(A) TO_STR_A(A) 
#define INFO_MSG TO_STR(__LINE__) ":" __FILE__ 

char buff[ 256 ] = { 0 }; 

sprintf(buff, "Something bad happened here, %s in function %s().", INFO_MSG, __func__); 
printf("INFO: %s\n", buff); 

... обратите внимание, что вызов __func__ можно сделать внутри самой функции. См. this.

+0

Действительно, вам нужно использовать '__func__' внутри самой функции, но вы можете предварительно обработать ее в первую очередь. –

+0

'__FILE__' уже строка; его не нужно стягивать. '__func__' не является функцией, поэтому ее нельзя вызвать; на самом деле, это предопределенный идентификатор, который указывает на строку, которая является именем функции, и действительна только внутри области действия функции. –

+0

@JonathanLeffler, исправленный ответ - спасибо за информативное наблюдение. –

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