Создайте экземпляры разных классов в соответствии с разными флагами компиляции одного и того же файла заголовка в С++

У меня есть класс, который зависит от флага компиляции. Я хотел бы создать экземпляры обеих версий класса в одной области.

my_class.h

#ifndef MY_CLASS_H
#define MY_CLASS_H

#ifdef COMP_FLAG
#define MY_CLASS MyClassFlagged
#else
#define MY_CLASS MyClassNotFlagged
#endif

class MY_CLASS
{
};

#endif

Затем я создал два заголовочных файла: my_class_flagged.h и my_class_not_flagged.h

my_class_flagged.h

#ifndef MY_CLASS_FLAGGED
#define MY_CLASS_FLAGGED

#include "my_class.h"

#endif
my_class_not_flagged.h

#ifndef MY_CLASS_NOT_FLAGGED
#define MY_CLASS_NOT_FLAGGED

#include "my_class.h"

#endif

Я использую bazel в качестве системы сборки, поэтому скомпилировал их с COMP_FLAG и без него, чтобы создать две отдельные цели.

BUILD

cc_library(
    name = "not_flagged",
    hdrs = ["my_class.h"],
)

cc_library(
    name = "flagged",
    hdrs = ["my_class.h"],
    defines = ["COMP_FLAG"],
)

cc_library(
    name = "my_class_flagged",
    hdrs = ["my_class_flagged.h"],
    deps = [":flagged"],
)

cc_library(
    name = "my_class_not_flagged",
    hdrs = ["my_class_not_flagged.h"],
    deps = [":not_flagged"],
)

Тогда я не могу создать экземпляры обоих из них, а только один:

#include "my_class_flagged.h"
#include "my_class_not_flagged.h"

// NOT BUILD
TEST(MyClassNotFlaggedTest, NotFlaggedInstantiation)
{
    MyClassNotFlagged my_class_not_flagged;
    std::ignore = my_class_not_flagged;
}

// PASS
TEST(MyClassFlaggedTest, FlaggedInstantiation)
{
    MyClassFlagged my_class_flagged;
    std::ignore = my_class_flagged;
}

получение этой ошибки: error: 'MyClassNotFlagged' was not declared in this scope; did you mean 'MyClassFlagged'?

где я использовал:

cc_test(
    name = "test",
    srcs = ["test.cpp"],
    deps = [
        ":my_class_flagged",
        ":my_class_not_flagged",
    ],
)

Попытки решения

[463035818_is_not_an_ai] Проблема с защитой включения

Я попытался изменить файл my_class.h:

#ifdef COMP_FLAG
#ifndef MY_CLASS_FLAGGED_H
#define MY_CLASS_FLAGGED_H

#define MY_CLASS MyClassFlagged

class MY_CLASS
{
};

#endif  // MY_CLASS_FLAGGED_H
#else
#ifndef MY_CLASS_NOT_FLAGGED_H
#define MY_CLASS_NOT_FLAGGED_H

#define MY_CLASS MyClassNotFlagged

class MY_CLASS
{
};

#endif  // MY_CLASS_NOT_FLAGGED_H
#endif

Однако теперь у меня оба теста не строятся:

error: 'MyClassNotFlagged' was not declared in this scope

error: 'MyClassFlagged' was not declared in this scope.

как это не работает? С какой проблемой вы столкнулись?

463035818_is_not_an_ai 20.06.2024 13:32

Также покажите нам соответствующее содержимое my_class_flagged.h и my_class_not_flagged.h. Кстати, #inlude с одинарной кавычкой (') мне не знаком.

wohlstad 20.06.2024 13:32
#include 'my_class_flagged.h' не похоже на правильную директиву включения, в противном случае опубликованный вами код выглядит нормально. Пожалуйста, подготовьте минимально воспроизводимый пример и укажите в вопросе все ошибки.
463035818_is_not_an_ai 20.06.2024 13:33

Один и тот же класс не может быть определен более одного раза в одном файле перевода. Что вы подразумеваете под версией того же класса? Они не могут быть одинаковыми. Вы можете использовать шаблоны. Непонятно, что вы здесь имеете в виду. Пожалуйста, предоставьте минимально воспроизводимый пример. Вы говорите: «Создайте экземпляр одного и того же класса с другим флагом компиляции», но в вашем вопросе у вас разные классы.

user12002570 20.06.2024 13:36

Каково содержание my_class_flagged.h и его непомеченного аналога? Это что-то иное, кроме #define и #include из my_class.h?

Botje 20.06.2024 13:37

@user12002570 user12002570 они определяют два разных класса

463035818_is_not_an_ai 20.06.2024 13:37

@463035818_is_not_an_ai Да, я понимаю, поэтому и спросил. В своем вопросе они сказали: «Обе версии класса» и «Создание экземпляра одного и того же класса разные». Смотрите обновленный комментарий.

user12002570 20.06.2024 13:38
MYC_CLASS, вероятно, проблема. Исправьте опечатку. Проблема решается?
Eljay 20.06.2024 13:39

Я обновил пост. Я думал, что таким образом компилятор сможет создать два разных объектных файла во время компиляции, и это не создаст проблем на этапе компоновки. Однако это похоже на то, что он компилируется с использованием только одного флага компиляции. Если я попытаюсь создать упомянутые выше заголовочные файлы, это сработает.

Francesco Vezzi 20.06.2024 13:49

@Элджей, спасибо, что заметил. К сожалению, это всего лишь упрощенная версия моей исходной проблемы.

Francesco Vezzi 20.06.2024 13:50

В этом случае наиболее полезным будет минимальный воспроизводимый пример, а не пример кода, который не воспроизводит проблему. (Помогите нам помочь вам.)

Eljay 20.06.2024 13:51

когда вы создаете упрощенный пример, обязательно проделайте весь путь, попробуйте скомпилировать его и посмотрите, что произойдет. Пока вы показываете нам только код, в котором есть другие несвязанные опечатки, например, #include 'my_class_flagged.h', код, который вы нам показываете, не является кодом, в котором есть проблема, с которой вам нужна помощь, следовательно, мы не можем помочь вам с вашей реальной проблемой.

463035818_is_not_an_ai 20.06.2024 14:00

спасибо за все ваши предложения, я улучшил вопрос и теперь думаю, что он намного лучше.

Francesco Vezzi 20.06.2024 14:10

сообщение об ошибке компилятора отсутствует

463035818_is_not_an_ai 20.06.2024 14:17

ваш my_class.h имеет защиту заголовка, что означает, что после того, как вы включили my_class_flagged.h, включение my_class_not_flagged.h не увидит содержимое my_class.h.

463035818_is_not_an_ai 20.06.2024 14:18

Какую реальную проблему вы пытаетесь решить? Зачем вам нужны два разных класса с разными именами, но в остальном идентичные?

463035818_is_not_an_ai 20.06.2024 14:20

в моей реальной проблеме MyClass намного сложнее, и в зависимости от флага компиляции некоторые методы немного меняются.

Francesco Vezzi 20.06.2024 14:24

@463035818_is_not_an_ai спасибо за эту интуицию. Я попробовал обходной путь на его основе и опубликую его.

Francesco Vezzi 20.06.2024 14:30

как уже упоминалось, в C++ есть шаблоны, и я почти уверен, что то, что вы хотите, можно сделать гораздо проще внутри языка, чем за его пределами. Все, что вы делаете с препроцессором, является загадочным, и (почти) все, что вы делаете с вашей системой сборки, специфично для вашей системы сборки, в то время как использование шаблонов для одного и того же будет работать везде.

463035818_is_not_an_ai 20.06.2024 14:33

Я не понимаю смысла последнего редактирования. Вместо одного включающего охранника теперь их два.... и MY_CLASS теперь появляется дважды

463035818_is_not_an_ai 20.06.2024 14:42

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

Francesco Vezzi 20.06.2024 14:44

да, моя идея заключалась в том, чтобы попытаться включить защиту условного включения на основе флага компиляции. если я их удалю, у меня возникнут проблемы с несколькими определениями

Francesco Vezzi 20.06.2024 14:47

Почему в вашем обновленном примере кода my_class.h имеет защиту заголовка, если его можно включать несколько раз? Почему my_class_flagged.h нет #define COMP_FLAG? Вы протестировали этот пример кода, чтобы убедиться, что он воспроизводит вашу проблему?

Eljay 20.06.2024 14:57

@KamilCuk спасибо за предложения, к сожалению, я думаю, что этот подход потребует от меня определения как MyClassFlagged, так и MyClassNotFlagged. Я хотел бы пометить класс, ничего не переопределяя.

Francesco Vezzi 20.06.2024 15:01

У @eljay my_class_flagged.h нет #define COMP_FLAG, потому что он включает цель :flagged в файл BUILD, который ее содержит. Я попробовал добавить его, и у меня возникла проблема с переопределением

Francesco Vezzi 20.06.2024 15:11

Я ничего не знаю о Базеле. Я не понимаю, как Bazel может определить флаг для одного файла заголовка и не определить флаг для другого файла заголовка в одной единице перевода.

Eljay 20.06.2024 18:21
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
26
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

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

Вы можете определить один шаблон класса:

template <int tag> 
struct MY_CLASS {};   

using MyClassFlagged = MY_CLASS<0>;
using MyClassNotFlagged = MY_CLASS<1>;

Существуют способы определить разницу между этим подходом и определением двух отдельных (не шаблонных) классов, но для большинства применений и приложений эта разница не должна иметь значения.


Если вы хотите остаться с препроцессором (по каким-либо причинам), я предлагаю хотя бы исключить систему сборки. Дважды включите заголовок, явно определите и отмените определение флага в своем коде.

В my_class_flagged.h напишите:

#ifndef MY_CLASS_FLAGGED
#define MY_CLASS_FLAGGED

#define COMP_FLAG
#include "my_class.h"
#undef COMP_FLAG

#endif

И в my_class_not_flagged.h напишите:

#ifndef MY_CLASS_NOT_FLAGGED
#define MY_CLASS_NOT_FLAGGED

#include "my_class.h"

#endif

И удалите защиту включения из my_class.h. И я предлагаю переименовать его, например, в my_class.x или в другое, отличное от .h, чтобы указать, что он не должен быть включен напрямую.

И в my_class.h в последней строке перед закрытием защиты заголовка добавьте:

#undef MY_CLASS

Если вы это сделаете, вы можете включить оба заголовка (в любом порядке) в main.cpp, чтобы определить оба класса.

Живая демо

к сожалению, это не решает мой первоначальный вопрос, мне нравится этот подход, и я хотел бы иметь возможность шаблонизировать этот класс. Спасибо.

Francesco Vezzi 20.06.2024 14:51

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

463035818_is_not_an_ai 20.06.2024 15:03

@FrancescoVezzi Я добавил нешаблоновый подход, более похожий на ваш

463035818_is_not_an_ai 20.06.2024 15:10

большое спасибо, я попробовал, но проблема осталась та же: error: 'MyClassNotFlagged' was not declared in this scope; did you mean 'MyClassFlagged'

Francesco Vezzi 20.06.2024 15:16

возможно, это просто невозможно

Francesco Vezzi 20.06.2024 15:16

возможно, вы все еще компилируете код, отличный от того, что вы здесь показываете, или вы не следовали моим инструкциям должным образом. Вы удалили защиту включения из my_class.h?

463035818_is_not_an_ai 20.06.2024 15:20

Я пытаюсь использовать вышеупомянутый код, и да, я забыл удалить защиту включения. Однако я все еще получаю эту ошибку переопределения: error: "MY_CLASS" redefined #define MY_CLASS MyClassNotFlaggednote: this is the location of the previous definition 5 | #define MY_CLASS MyClassFlagged

Francesco Vezzi 20.06.2024 15:23

это однофайловый эквивалент решения, которое я предлагаю godbolt.org/z/KcqW1x4rf. Есть ошибка, но was not declared не первая

463035818_is_not_an_ai 20.06.2024 15:24

@FrancescoVezzi Я добавил исправление.

463035818_is_not_an_ai 20.06.2024 15:26

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