struct Test1 : public Base {
enum { type = 1 };
int a;
char ch;
virtual void func1();
};
struct Test2 : public Base {
enum { type = 2 };
int a;
char ch;
virtual void func1();
};
Я разрабатываю проект с С++ 14. По какой-то причине совместимости я должен объявить два класса, как указано выше, члены которых точно такие же.
Мой вопрос в том, есть ли какой-то метод метапрограммирования, который позволяет мне проверить, имеют ли два класса одинаковые члены?
реальная проблема
Да, я знаю, что это так странно, но у меня возникла такая проблема...
Я разрабатываю веб-сервер, каждый вышеприведенный класс представляет собой протокол.
Теперь проблема в том, что какой-то разработчик Android неправильно использовал протокол Test1
, поэтому мы не можем его трогать, поскольку пользователи могут не обновлять свои приложения. Что я мог бы сделать, так это просто добавить еще один протокол. Поэтому я добавляю класс Test2
.
Поскольку два протокола делают одно и то же, я хочу убедиться, что они всегда одинаковы. Это означает, что если кто-то когда-нибудь добавит элемент в Test1
, но забудет добавить член в Test2
, я хочу получить ошибку времени компиляции.
Кстати, я забочусь только о члене данных, а не о функции-члене.
@user7860670 user7860670 Вы имеете в виду, что член перечисления отличается?
Нет, я имею в виду, что все участники разные.
Остерегайтесь проблема XY. Зачем вам два совершенно одинаковых класса?
Учитывая конкретное имя члена, вы, вероятно, можете проверить, содержат ли два разных класса этот член.
@Thomas Я разрабатываю веб-сервер, разработчик Android сказал мне, что мне нужно добавить новый класс для того же API (который на самом деле является протоколом) из-за некоторых проблем с совместимостью.
Это не имеет смысла для меня. Почему внутренности вашего веб-сервера C++ может повлиять на код клиента Android? На уровне HTTP нет такого понятия, как класс.
@user7860670 user7860670 Я пытался наследовать базовый класс, но моя проблема в том, что я должен ВСЕГДА сохранять одни и те же члены двух классов. Наследство не может этого гарантировать. Я имею в виду, что если когда-нибудь разработчик добавит члена для одного из них, я хочу сгенерировать ошибку времени компиляции.
@Thomas Это все потому, что какой-то разработчик неправильно использовал протокол три года назад. Теперь я не могу изменить его, потому что некоторые пользователи могут никогда не обновлять свое приложение, понимаете, о чем я? Я должен убедиться, что старая версия всегда работает. Поэтому я не могу изменить старый протокол.
Класс - это не протокол. Но давайте представим, что это так. Тогда дублирование может быть прекрасным решением: один из классов предназначен для обратной совместимости и никогда не изменится; другой движется вперед и может быть изменен. Так зачем тогда нужна проверка, что они полностью идентичны?
С этой логикой, если когда-нибудь разработчик добавит элемент в оба из них, то генерация ошибки времени компиляции c будет невозможна. И, скорее всего, вам все равно не нужны два класса. Распространение копипасты никогда не было хорошей идеей.
@Thomas У моей компании есть собственная система протоколов. С точки зрения бэкэнд-разработчика, класс с уникальным перечислением — это просто протокол.
Is it possible to check if two classes have the same members
Если вы можете перейти на С++ 20, и если классы имеют стандартную компоновку, и вас беспокоят не точные совпадения для типов, а скорее совместимость компоновки, например, один класс может иметь константный член, а другой - не- const — тогда вы можете использовать черту типа std::is_layout_compatible
.
В противном случае вам, возможно, придется прибегнуть к метапрограммированию.
I want to make sure that they are always the same.
Что касается проблемы XY, если вы можете изменить Test1
совместимым с API способом, то наследование может быть решением:
struct TestData : Base {
int a;
char ch;
};
template<int type>
struct TestBase : TestData {
enum { type = type };
};
struct Test1 : TestBase<1> {
void func1();
};
struct Test2 : TestBase<2> {
void func1();
};
Если вы вообще не можете прикасаться к Test1
, то хакерским обходным путем будет использование его в качестве основы:
struct Test2 : Test1 {
enum { type = 2 };
void func1();
};
Это не идеально из-за неявных преобразований. Вы можете избежать этого, используя частное наследование, но тогда у вас возникнет проблема с общим публичным не виртуальным Base
.
Хороший. Извините, я только что отредактировал свой вопрос, и классы содержат виртуальные функции... Так что я думаю, что они больше не являются стандартным макетом. Виноват. Я забыл std::is_layout_compatible
. Хороший мужчина.
Мне не нравится предпосылка этого вопроса, потому что он, по сути, требует способа сохранить дублирование кода. Однако на практике случается дерьмо, и если кому-то нужны два класса с одинаковым содержимым, лучше было бы не объявлять два класса, а затем проверять их на совместимость, а объявлять их только один раз. Это можно сделать с помощью базового класса или с помощью препроцессора. Последний случай также предотвратит проникновение новых членов в производные классы, в то время как добавление новых членов в оба класса требует только одной модификации:
#define MAKE_CLASS(mp_name) \
struct mp_name : public Base { \
enum { type = 1 }; \
int a; \
char ch; \
void func1(); \
};
MAKE_CLASS(test1)
MAKE_CLASS(test2)
Эти классы состоят из разных членов. Если вам нужны два класса с точно такими же членами, вам следует создать базовый класс со всеми членами, а затем наследовать его, не добавляя ничего нового.