У меня есть класс, который зависит от флага компиляции. Я хотел бы создать экземпляры обеих версий класса в одной области.
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",
],
)
Я попытался изменить файл 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.
Также покажите нам соответствующее содержимое my_class_flagged.h и my_class_not_flagged.h. Кстати, #inlude с одинарной кавычкой (') мне не знаком.
#include 'my_class_flagged.h' не похоже на правильную директиву включения, в противном случае опубликованный вами код выглядит нормально. Пожалуйста, подготовьте минимально воспроизводимый пример и укажите в вопросе все ошибки.
Один и тот же класс не может быть определен более одного раза в одном файле перевода. Что вы подразумеваете под версией того же класса? Они не могут быть одинаковыми. Вы можете использовать шаблоны. Непонятно, что вы здесь имеете в виду. Пожалуйста, предоставьте минимально воспроизводимый пример. Вы говорите: «Создайте экземпляр одного и того же класса с другим флагом компиляции», но в вашем вопросе у вас разные классы.
Каково содержание my_class_flagged.h и его непомеченного аналога? Это что-то иное, кроме #define и #include из my_class.h?
@user12002570 user12002570 они определяют два разных класса
@463035818_is_not_an_ai Да, я понимаю, поэтому и спросил. В своем вопросе они сказали: «Обе версии класса» и «Создание экземпляра одного и того же класса разные». Смотрите обновленный комментарий.
MYC_CLASS, вероятно, проблема. Исправьте опечатку. Проблема решается?
Я обновил пост. Я думал, что таким образом компилятор сможет создать два разных объектных файла во время компиляции, и это не создаст проблем на этапе компоновки. Однако это похоже на то, что он компилируется с использованием только одного флага компиляции. Если я попытаюсь создать упомянутые выше заголовочные файлы, это сработает.
@Элджей, спасибо, что заметил. К сожалению, это всего лишь упрощенная версия моей исходной проблемы.
В этом случае наиболее полезным будет минимальный воспроизводимый пример, а не пример кода, который не воспроизводит проблему. (Помогите нам помочь вам.)
когда вы создаете упрощенный пример, обязательно проделайте весь путь, попробуйте скомпилировать его и посмотрите, что произойдет. Пока вы показываете нам только код, в котором есть другие несвязанные опечатки, например, #include 'my_class_flagged.h', код, который вы нам показываете, не является кодом, в котором есть проблема, с которой вам нужна помощь, следовательно, мы не можем помочь вам с вашей реальной проблемой.
спасибо за все ваши предложения, я улучшил вопрос и теперь думаю, что он намного лучше.
сообщение об ошибке компилятора отсутствует
ваш my_class.h имеет защиту заголовка, что означает, что после того, как вы включили my_class_flagged.h, включение my_class_not_flagged.h не увидит содержимое my_class.h.
Какую реальную проблему вы пытаетесь решить? Зачем вам нужны два разных класса с разными именами, но в остальном идентичные?
в моей реальной проблеме MyClass намного сложнее, и в зависимости от флага компиляции некоторые методы немного меняются.
@463035818_is_not_an_ai спасибо за эту интуицию. Я попробовал обходной путь на его основе и опубликую его.
как уже упоминалось, в C++ есть шаблоны, и я почти уверен, что то, что вы хотите, можно сделать гораздо проще внутри языка, чем за его пределами. Все, что вы делаете с препроцессором, является загадочным, и (почти) все, что вы делаете с вашей системой сборки, специфично для вашей системы сборки, в то время как использование шаблонов для одного и того же будет работать везде.
Я не понимаю смысла последнего редактирования. Вместо одного включающего охранника теперь их два.... и MY_CLASS теперь появляется дважды
вы совершенно правы, проблема в том, что, к сожалению, я имею дело с устаревшим кодом. Код работает очень хорошо, поскольку изменения минимальны. В целях тестирования мне нужно было бы создать экземпляры обоих классов в одной области, и только здесь у меня возникают проблемы.
да, моя идея заключалась в том, чтобы попытаться включить защиту условного включения на основе флага компиляции. если я их удалю, у меня возникнут проблемы с несколькими определениями
Почему в вашем обновленном примере кода my_class.h имеет защиту заголовка, если его можно включать несколько раз? Почему my_class_flagged.h нет #define COMP_FLAG? Вы протестировали этот пример кода, чтобы убедиться, что он воспроизводит вашу проблему?
@KamilCuk спасибо за предложения, к сожалению, я думаю, что этот подход потребует от меня определения как MyClassFlagged, так и MyClassNotFlagged. Я хотел бы пометить класс, ничего не переопределяя.
У @eljay my_class_flagged.h нет #define COMP_FLAG, потому что он включает цель :flagged в файл BUILD, который ее содержит. Я попробовал добавить его, и у меня возникла проблема с переопределением
Я ничего не знаю о Базеле. Я не понимаю, как Bazel может определить флаг для одного файла заголовка и не определить флаг для другого файла заголовка в одной единице перевода.





Боюсь, это не ответ, потому что из комментариев уже видно, что ваш пример слишком упрощен. В любом случае, похоже, что вы превращаете что-то очень простое в нечто сложное. Используйте язык, когда можете, прибегайте к условной компиляции с использованием препроцессора только при необходимости. Ничто в вашем примере действительно не нуждается в препроцессоре.
Вы можете определить один шаблон класса:
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, чтобы определить оба класса.
к сожалению, это не решает мой первоначальный вопрос, мне нравится этот подход, и я хотел бы иметь возможность шаблонизировать этот класс. Спасибо.
@FrancescoVezzi, как уже упоминалось, я убежден, что мы наблюдаем проблему XY, и я думаю, что было бы полезно, если бы вместо того, чтобы спрашивать о том, что, по вашему мнению, может быть решением, расскажите нам о реальной проблеме. В любом случае, вопрос прошел несколько итераций, и я, честно говоря, думаю, что это ответ на вопрос в его нынешнем виде. По крайней мере, ничто в вашем вопросе не указывает на то, что это не так.
@FrancescoVezzi Я добавил нешаблоновый подход, более похожий на ваш
большое спасибо, я попробовал, но проблема осталась та же: error: 'MyClassNotFlagged' was not declared in this scope; did you mean 'MyClassFlagged'
возможно, это просто невозможно
возможно, вы все еще компилируете код, отличный от того, что вы здесь показываете, или вы не следовали моим инструкциям должным образом. Вы удалили защиту включения из my_class.h?
Я пытаюсь использовать вышеупомянутый код, и да, я забыл удалить защиту включения. Однако я все еще получаю эту ошибку переопределения: error: "MY_CLASS" redefined #define MY_CLASS MyClassNotFlaggednote: this is the location of the previous definition 5 | #define MY_CLASS MyClassFlagged
это однофайловый эквивалент решения, которое я предлагаю godbolt.org/z/KcqW1x4rf. Есть ошибка, но was not declared не первая
@FrancescoVezzi Я добавил исправление.
как это не работает? С какой проблемой вы столкнулись?