Pertanyaan Memperlakukan __func__ sebagai string literal, bukan identifier yang telah ditentukan sebelumnya


Saya menggunakan gcc untuk mengkompilasi kode C99. Saya ingin menulis makro yang akan mengembalikan string yang berisi nama fungsi dan nomor baris.

Ini yang saya miliki:

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

Namun, ketika saya mengkompilasi kode yang mencoba menggunakan string ini, misalnya:

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

Saya mendapatkan pesan kesalahan berikut:

error: expected ‘)’ before ‘__func__’

Saya telah melacak masalah ke makro. seperti ketika saya menghapus __func__ dari makro, kode dikompilasi dengan benar.

Bagaimana cara memperbaiki makro, sehingga saya dapat menyertakan yang telah ditentukan __func__ makro dalam string saya?


14
2017-08-18 12:00


asal


Jawaban:


Dilihat dari komentar Anda, tujuannya adalah untuk memiliki makro yang menggabungkan nama file dan nama fungsi (dan mungkin nomor baris) ke dalam string tunggal yang dapat dilewatkan sebagai argumen untuk fungsi seperti printf() atau strcpy() atau syslog().

Sayangnya, saya pikir itu tidak mungkin.

Standar C11 mengatakan:

ISO / IEC 9899: 2011 §6.4.2.2 Pengenal yang ditentukan sebelumnya

¶1 Pengidentifikasi __func__ harus secara implisit dideklarasikan oleh penerjemah seolah-olah, segera mengikuti tanda kurung kurawal masing-masing definisi fungsi, deklarasi

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

muncul, di mana fungsi-nama adalah nama fungsi lexically-melampirkan.

Karena itu, __func__ bukan makro, tidak seperti __FILE__ atau __LINE__.

Pertanyaan yang terkait Apa perbedaannya __PRETTY_FUNCTION__, __FUNCTION__, __func__? mencakup beberapa nama alternatif. Ini adalah ekstensi khusus GCC, bukan nama standar. Selain itu, dokumentasi GCC 4.8.1 mengatakan:

Pengidentifikasi ini bukan makro preprocessor. Dalam GCC 3.3 dan sebelumnya, dalam C saja, __FUNCTION__ dan __PRETTY_FUNCTION__ diperlakukan sebagai literal string; mereka bisa digunakan   untuk menginisialisasi array char, dan mereka bisa digabung dengan string literal lainnya. GCC   3.4 dan kemudian memperlakukan mereka sebagai variabel, seperti __func__. Di C ++, __FUNCTION__ dan __PRETTY_FUNCTION__ selalu menjadi variabel.

Ada alasan kuat mengapa ini tidak bisa menjadi konstruksi preprocessor. The preprocessor tidak tahu apa fungsi dan apakah teks yang diproses dalam lingkup fungsi, atau apa nama fungsi melampirkan. Ini adalah prosesor teks sederhana, bukan kompilator. Jelas, adalah mungkin untuk membangun pemahaman yang banyak ke dalam preprocessor (semata-mata untuk mendukung fitur yang satu ini), tetapi ini tidak diperlukan oleh standar, dan tidak seharusnya itu diperlukan oleh standar.

Sayangnya, saya pikir ini berarti bahwa upaya untuk menggabungkan __func__ (dengan ejaan apa saja) dengan __FILE__ dan __LINE__ dalam satu makro untuk menghasilkan satu string literal yang dikutuk.

Jelas, Anda dapat menghasilkan nama file dan nomor baris sebagai string menggunakan mekanisme makro dua langkah standar:

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

#define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__)

Anda tidak bisa mendapatkan nama fungsi sebagai bagian dari string literal.

Ada argumen bahwa nama file dan nomor baris cukup untuk mengidentifikasi di mana masalahnya; nama fungsi hampir tidak diperlukan. Ini lebih kosmetik daripada fungsional, dan sedikit membantu pemrogram tetapi bukan pengguna lain.


18
2017-08-18 16:40



Setelah eksperimen singkat saya menemukan bahwa Anda tidak dapat menggunakannya __func__ dengan stringification. Tidak akan masuk akal jika Anda bisa karena ini berarti bahwa nilainya akan dimanapun makro didefinisikan, bukan di mana itu diterapkan.

Sifat dari __func__, sebagaimana tercantum dalam komentar pada pertanyaan, dijelaskan dalam jawaban ini.

Stringification dilakukan pada waktu pra-prosesor dan karena itu __func__ tidak tersedia karena pada dasarnya adalah fungsi string lokal yang didefinisikan nanti pada proses kompilasi.

Namun Anda bisa menggunakannya __func__ secara makro selama Anda tidak menggunakan stringification di atasnya. Saya pikir yang berikut ini melakukan apa yang Anda cari:

#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;
}

Perhatikan bahwa tidak ada alasan khusus, dalam pertanyaan seperti yang disajikan, untuk menggunakan buffer string. Pengikut main fungsi akan mencapai efek yang sama tanpa kemungkinan buffer overrun:

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

Secara pribadi, saya akan menyelesaikan seluruh proses dalam makro seperti ini:

#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
2017-08-18 12:34



Ingat itu, "__func__ bukan fungsi sehingga tidak bisa disebut; sebenarnya, itu adalah identifier yang telah ditentukan yang menunjuk ke string yang merupakan nama fungsi, dan hanya berlaku di dalam lingkup fungsi."- Jonathan.

Berikut ini yang Anda cari:

#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 );

... perhatikan bahwa panggilan ke __func__ dapat dibuat di dalam fungsi itu sendiri. Lihat ini.


3
2017-08-18 12:42



itu adalah kesalahan sintaks. Saya mencoba untuk datang dengan spesifikasi makro Anda tetapi saya tidak menemukan cara yang efisien, jadi mungkin Anda dapat mencoba ini:

#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);
}

1
2017-08-18 12:25