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

Обработка __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__?

4b9b3361

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