C странность препроцессора странности - PullRequest
5 голосов
/ 12 сентября 2011

Я определяю макрос, который оценивается как постоянная строка, содержащая имя файла и номер строки, для целей ведения журнала.

Работает нормально, но я просто не могу понять, зачем нужны 2 дополнительных макроса - STRINGIFY и TOSTRING, когда интуиция подсказывает просто __FILE__ ":" #__LINE__.

#include <stdio.h>

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define THIS_ORIGIN (__FILE__ ":" TOSTRING(__LINE__))

int main (void) {
  /* correctly prints "test.c:9" */
  printf("%s", THIS_ORIGIN);
  return 0;
}

Мне это кажется уродливым хаком.

Может ли кто-нибудь подробно объяснить, что происходит поэтапно, так что __LINE__ корректируется в строку и почему ни один из __FILE__ ":" STRINGIFY(__LINE__) и __FILE__ ":" #__LINE__ не работает?

Ответы [ 2 ]

7 голосов
/ 12 сентября 2011

Из-за порядка расширения.Документация GCC гласит:

Макро-аргументы полностью макроразвертываются перед тем, как их подставить в тело макроса, если они не будут преобразованы в строку или вставлены с другими токенами.После подстановки все тело макроса, включая замещенные аргументы, снова сканируется для раскрытия макросов.В результате аргументы сканируются дважды, чтобы расширить в них вызовы макросов.

Поэтому, если аргумент будет строковым, он не будет расширен в первую очередь.Вы получаете буквальный текст в скобках.Но если он передается другому макросу, он расширяется.Поэтому, если вы хотите расширить его, вам нужно два уровня макросов.

Это сделано потому, что есть случаи, когда вы не хотите расширить аргумент перед строкой, чаще всего этоassert() макрос.Если вы пишете:

assert(MIN(width, height) >= 240);

, вы хотите, чтобы сообщение было:

Assertion MIN(width, height) >= 240 failed

, а не какой-то безумной вещью, в которую расширяется макрос MIN (в gcc он использует несколько специфичных для gcc расширений идовольно долго IIRC).

2 голосов
/ 12 сентября 2011

Вы не можете просто использовать __FILE__":"#__LINE__, потому что оператор stringify # может применяться только к параметру макроса.

__FILE__ ":" STRINGIFY(__LINE__) будет работать нормально с другим текстом (например, __FILE__ ":" STRINGIFY(foo), но не работает при использовании с другим макросом (который на самом деле __LINE__) в качестве параметра, в противном случае этот макрос не подставляется .

...