Почему g / clang выдает ошибку «не выдает допустимый токен предварительной обработки»

#c #g #c-preprocessor #clang #cl

Вопрос:

Ниже приведен минимальный код для воспроизведения проблемы.

 #include <chrono>
#include <functional>
#include <iostream>

using namespace std;

class Test {
public:
  Test() {}
  void test1() { cout << __func__ << endl; }
  void test2() { cout << __func__ << endl; }
  void testPrepare() { cout << __func__ << endl; }

private:
};
#define DO_TEST(obj, testName)                                                 
  {                                                                            
    obj.testPrepare();                                                         
    std::function<void(void)> test = std::bind(amp;Test::##testName, obj);        
    /*test();                                                                  
    Some other code which use test()*/                                         
  }

int main(int argc, char const *argv[]) {
  Test obj;
  DO_TEST(obj, test1);
  DO_TEST(obj, test2);
  /* code */
  return 0;
}
 

Это работает хорошо и делает то, что ожидается с cl.exe , но на g / clang выдает ошибку времени компиляции, как показано ниже:

 g   d.cpp
d.cpp:19:53: error: pasting "::" and "test1" does not give a valid preprocessing token
   19 |     std::function<void(void)> test = std::bind(amp;Test::##testName, obj);        
      |                                                     ^~
d.cpp:26:3: note: in expansion of macro ‘DO_TEST’
   26 |   DO_TEST(obj, test1);
      |   ^~~~~~~
d.cpp:19:53: error: pasting "::" and "test2" does not give a valid preprocessing token
   19 |     std::function<void(void)> test = std::bind(amp;Test::##testName, obj);        
      |                                                     ^~
d.cpp:27:3: note: in expansion of macro ‘DO_TEST’
   27 |   DO_TEST(obj, test2);
      |   ^~~~~~~
 

Сведения о компиляторе:

  • cl.exe Оптимизирующий компилятор Microsoft (R) C/ C версии 19.29.30133 для x86
  • clang версия 10.0.0-4ubuntu1, Цель: x86_64-pc-linux-gnu
  • g (Ubuntu 9.3.0-17ubuntu1 ~ 20.04) 9.3.0

Примечание: я знаю, что изменение amp;Test::##testName на amp;Test:: testName решит проблему. Но я хочу понять, является ли это ошибкой cl.exe чтобы разрешить приведенный выше код или если g / clang выдает ошибку, это ошибка.

Комментарии:

1. Я думаю, что ошибки верны, ::foo сами по себе они не являются допустимым токеном, и они должны быть 2 разными токенами. amp;Test::testName вероятно, это правильно. Не верьте мне на слово, я не уверен на 100%.

2. @mediocrevegetable1 итак cl.exe неправильно ли разрешать компиляцию с amp;Test::##testName помощью, не так ли?

3. ИМО, это неправильно. Опять же, я не уверен на 100%.

4. В дополнение к предложению @mediocrevegetable1: сделайте a do ... while(0) вокруг макросов ( пример ), иначе при использовании макросов вы ставите a ; после конца блока } , что неверно.

5. Чтобы включить режим соответствия препроцессору, используйте /Zc:preprocessor . При использовании этой опции компилятор выдает предупреждение.

Ответ №1:

В соответствии со спецификацией C:

каждый экземпляр токена предварительной обработки ## в списке замены удаляется, а предыдущий токен предварительной обработки объединяется со следующим токеном предварительной обработки. … Если результат не является допустимым токеном предварительной обработки, поведение не определено.

Таким образом, использование ## таким образом, чтобы не создавать ни одного токена, не определено — компилятор может дать диагностику об этом, но не требуется, и вместо этого он может (как расширение) сделать что-то еще.