Можно ли проверить, имеют ли два класса одинаковые члены

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 16.03.2022 10:56

@user7860670 user7860670 Вы имеете в виду, что член перечисления отличается?

Yves 16.03.2022 10:57

Нет, я имею в виду, что все участники разные.

user7860670 16.03.2022 10:58

Остерегайтесь проблема XY. Зачем вам два совершенно одинаковых класса?

Thomas 16.03.2022 10:59

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

Tootsie 16.03.2022 10:59

@Thomas Я разрабатываю веб-сервер, разработчик Android сказал мне, что мне нужно добавить новый класс для того же API (который на самом деле является протоколом) из-за некоторых проблем с совместимостью.

Yves 16.03.2022 11:00

Это не имеет смысла для меня. Почему внутренности вашего веб-сервера C++ может повлиять на код клиента Android? На уровне HTTP нет такого понятия, как класс.

Thomas 16.03.2022 11:03

@user7860670 user7860670 Я пытался наследовать базовый класс, но моя проблема в том, что я должен ВСЕГДА сохранять одни и те же члены двух классов. Наследство не может этого гарантировать. Я имею в виду, что если когда-нибудь разработчик добавит члена для одного из них, я хочу сгенерировать ошибку времени компиляции.

Yves 16.03.2022 11:03

@Thomas Это все потому, что какой-то разработчик неправильно использовал протокол три года назад. Теперь я не могу изменить его, потому что некоторые пользователи могут никогда не обновлять свое приложение, понимаете, о чем я? Я должен убедиться, что старая версия всегда работает. Поэтому я не могу изменить старый протокол.

Yves 16.03.2022 11:05

Класс - это не протокол. Но давайте представим, что это так. Тогда дублирование может быть прекрасным решением: один из классов предназначен для обратной совместимости и никогда не изменится; другой движется вперед и может быть изменен. Так зачем тогда нужна проверка, что они полностью идентичны?

Thomas 16.03.2022 11:11

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

user7860670 16.03.2022 11:11

@Thomas У моей компании есть собственная система протоколов. С точки зрения бэкэнд-разработчика, класс с уникальным перечислением — это просто протокол.

Yves 16.03.2022 11:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
12
76
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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. Хороший мужчина.

Yves 16.03.2022 11:19
Ответ принят как подходящий

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

#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)

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