Конкатенация строк препроцессора макросов C++

Я читал другие вопросы по этому поводу, но они либо не работают, либо я застрял.

Я пытаюсь динамически #import создать файл на основе флагов сборки, где имя моего импортированного файла имеет вид:

hardware_<hardware>_<version>.h

Я могу заставить это работать, но только без расширения файла .h, что не идеально.

Как мне изменить это, чтобы в конце стоял .h?

./hardware_XXX_YYY:

#define FOO "bar"

./main.cpp:

#include <iostream>   

#define HARDWARE XXX
#define HARDWARE_VERSION YYY

#define CCC(x) #x

// This fails:
//#define BBB(ha, ver) CCC(hardware_##ha##_##ver##.h)

// This works:
#define BBB(ha, ver) CCC(hardware_##ha##_##ver)

#define AAA(ha, ver) BBB(ha, ver)

#include AAA(HARDWARE, HARDWARE_VERSION)

int main() {
    
    std::cout << FOO  << '\n';
}

C++ не поддерживает раскрытие макросов в директиве #include в стандартной комплектации. Поэтому, если вас интересует какое-то нестандартное решение, вам следует указать, какой компилятор вы используете.

john 31.07.2024 10:42

Кстати, если вы опустите последний ## перед .h, он сгенерирует желаемый результат. #define BBB(ha, ver) CCC(hardware_##ha##_##ver.h)

zerocukor287 31.07.2024 10:44

@john Это поддерживается. См. en.cppreference.com/w/cpp/preprocessor/include пункт 3. Что не поддерживается, так это случаи, когда #include происходит из расширения макроса.

Weijun Zhou 31.07.2024 10:46

Препроцессор вроде бы формирует hardware_XXX_YYY. вместо hardware_XXX_YYY.h. Почему и почему первая форма не является допустимым токеном препроцессора?

Oersted 31.07.2024 10:58

Стандарт требует, чтобы вставка токена привела к созданию действительного токена. (описано, например, в Complete-concrete-concision.com/programming/c/… ) В общем, существует различие идентификаторов и знаков препинания. Таким образом, объединение последовательности символов (идентификатора?) с точкой (пунктуацией?) может оказаться недопустимым. На данный момент ответ на отбрасывание ## перед .h имеет какой-то смысл. (Должен признать слабую сторону всего этого рассуждения: я никогда не задумывался, как все это применимо к #include путям...)

Scheff's Cat 31.07.2024 11:13

@ 7478597 подразумевается, что если есть знак пунктуации, его левая и правая стороны должны быть действительным токеном? Это имело смысл, но я не мог уяснить это, прочитав стандарт. Вот почему удаление ## работает: hardware_##ha##_##ver дает действительный подтокен ", h также и они связаны пунктуатором? Это правильно?

Oersted 31.07.2024 11:18

Эмпирическое правило заключается в том, что если иначе он не сформирует другой действительный идентификатор, вам не нужен ##. ## в a##b необходим, потому что a и b в противном случае сформировали бы идентификатор ab (и, если возможно, расширились бы в макросе). ## в ver##.h не требуется, поскольку ver.h невозможно проанализировать как отдельный идентификатор.

Weijun Zhou 31.07.2024 11:30

Похоже на работу небольшого генератора кода, где вы создаете небольшой включаемый файл, содержащий для вас настоящий hardware_<hardware>_<version>.h. Этот заголовочный файл будет настолько маленьким, что вы, вероятно, сможете сгенерировать его из сценария, считывающего некоторую информацию о версии.

Pepijn Kramer 31.07.2024 12:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
8
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам не нужен дополнительный ## перед частью .h, поскольку .h не является частью анализа токена (ha и ver есть, и вам нужен дополнительный ## из-за подчеркивания).

Вот небольшой пример, включающий файл «string.h», созданный с помощью макросов. Опять же, st и .h не являются частью конкатенации, поэтому дополнительные ## не нужны.

#include <iostream>   

#define CCC(x) #x
#define BBB(ha, ver) CCC(st##ha##ver.h)

#include BBB(ri,ng)

int main() {
    
    std::cout << BBB(ri,ng)"\n";
}

Выход:
string.h

Я уверен, что в какой-то момент id попробовала эту перестановку, но она не сработала. Должно быть, у него была какая-то другая проблема в попытке заставить что-то работать. Спасибо

Andrew Bullock 31.07.2024 11:26
st является частью конкатенации. Другой вопрос, заменяется ли идентификатор в расширении макроса.
Weijun Zhou 31.07.2024 11:38

Другие вопросы по теме