Обработка __func__ как строкового литерала вместо предопределенного идентификатора
Я использую gcc для компиляции кода C99. Я хочу написать макрос, который вернет строку, содержащую имя функции и номер строки.
Это то, что у меня есть:
#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__
из макроса, код компилируется правильно.
Как исправить макрос, чтобы я мог включить в мою строку предопределенный макрос __func__
?
Ответы
Ответ 1
Судя по вашим комментариям, цель состоит в том, чтобы иметь макрос, который объединяет имя файла и имя функции (и, может быть, номер строки) в одну строку, которая может передаваться в качестве аргумента для таких функций, как 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__
.
Связанный вопрос В чем разница между __PRETTY_FUNCTION__
, __FUNCTION__
, __func__
? охватывает некоторые альтернативные имена. Это GCC-специфические расширения, а не стандартные имена. Кроме того, в документации GCC 4.8.1 говорится:
Эти идентификаторы не являются макросами препроцессора. В GCC 3.3 и ранее, только в C, __FUNCTION__
и __PRETTY_FUNCTION__
рассматривались как строковые литералы; они могут быть использованы для инициализации массивов char, и они могут быть объединены с другими строковыми литералами. НКУ 3.4 и выше рассматривают их как переменные, такие как __func__
. В С++ __FUNCTION__
и __PRETTY_FUNCTION__
всегда были переменными.
Есть веские причины, по которым они не могут быть конструкциями препроцессора. Препроцессор не знает, что такое функция, и является ли текст, который он обрабатывает, входит в область действия функции или то, что является именем закрывающей функции. Это простой текстовый процессор, а не компилятор. Ясно, что было бы возможно построить такое понимание в препроцессоре (только для поддержки этой одной функции), но стандарт не требуется, и он также не должен требоваться стандартом.
К сожалению, я думаю, это означает, что попытки объединить __func__
(посредством любой орфографии) с __FILE__
и __LINE__
в одном макросе для генерации единственного строкового литерала обречены.
Очевидно, вы можете сгенерировать имя файла и номер строки в виде строки с помощью стандартного двухступенчатого механизма макроса:
#define STR(x) #x
#define STRINGIFY(x) STR(x)
#define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__)
Вы не можете получить имя функции в это как часть строкового литерала.
Есть аргументы, что имя файла и номер строки достаточны, чтобы определить, где проблема; имя функции едва ли необходимо. Он более косметический, чем функциональный, и немного помогает программистам, но не другим пользователям.
Ответ 2
После быстрого эксперимента я обнаружил, что вы не можете использовать __func__
с строкой. Это не имело бы большого смысла, если бы вы могли, поскольку это означало бы, что значение будет везде, где макрос определен, вместо того, где он применяется.
Характер __func__
, как отмечено в комментариях к вопросу, описан в этом ответе.
Строка выполняется во время предварительного процессора и из-за этого __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;
}
Ответ 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__
может выполняться внутри самой функции. См. это.
Ответ 4
это синтаксическая ошибка. Я пытаюсь перейти с вашей спецификацией макроса, но я не нашел эффективного способа, поэтому, возможно, вы можете попробовать следующее:
#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);
}