Можно ли и / или нормально использовать #include, чтобы помещать большие куски повторяющегося кода в отдельный файл?

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

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

foo.h

class Foo
{
public:
virtual ~Foo() {};
#define FOO_VIRTUAL_IMPL = 0
#include "Foo_prototypes.h"};

Foo_prototypes.h

#if ! defined(FOO_VIRTUAL_IMPL)
# define FOO_VIRTUAL_IMPL
#endif

virtual void doSomething() FOO_VIRTUAL_IMPL;

virtual void doSomethingElse() FOO_VIRTUAL_IMPL;

Использует ли макрос определения, подобный этому, также распространенный (то есть
включаемый файл, который будет использоваться для чисто виртуальных и обычных виртуальных функций)? Используются ли такие вещи часто, или просто для маленьких хаков, чтобы сэкономить немного времени / усилий?

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

Рассматриваемый код — это C ++ API Interactive Brokers, если кто-то хочет увидеть его в контексте. Соответствующие файлы: EWrapper.h и TestCppClient.h и EWrapper_prototypes.h.

7

Решение

Более безопасная реализация будет:

#define FOO_PROTOTYPES(FOO_VIRTUAL_IMPL)\
virtual void doSomething() FOO_VIRTUAL_IMPL;\
virtual void doSomethingElse() FOO_VIRTUAL_IMPL;

class Foo
{
public:
virtual ~Foo() {};
FOO_PROTOTYPES( = 0 );
};

class FooImpl : public Foo
{
public:
virtual ~FooImpl() {};
FOO_PROTOTYPES( override );
};

Все находится в одном заголовке и позволяет избежать случайного использования FOO_VIRTUAL_IMPL значение определено в одном заголовке в другом.

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

1

Другие решения

Wellll, это не совсем то, что я бы назвал хорошо разработанным кодом. Да, технически вы можете включить заголовки в такое место, но это не совсем стандартно, поэтому я бы не рекомендовал это. Просто в общем, «держись подальше от пыльных уголков языка». Однако, если вы действительно хотите делать такие вещи, вы можете. Однако несоблюдение стандартной практики может иметь два эффекта.

  1. Нестандартный код, как правило, труднее читать, поэтому меньше людей смогут или захотят внести свой вклад.

  2. Нестандартный код может содержать больше ошибок, которые не так заметны, просто потому, что он не стандартный.

Примером ошибок, о которых я упоминал заранее, является способ FOO_VIRTUAL_IMPL работает. #defineОн не ограничен областью действия, так что это будет видно всему вашему коду. Было бы очень легко #define это в одном заголовке, и не определять его вообще в другом заголовке. Это приведет к тому, что все виртуальные функции во втором заголовке будут чисто виртуальными, вероятно, не такими, как вы предполагали.

РЕДАКТИРОВАТЬ: Кроме того, как сказал Калет, если ваш класс требует столько повторяющихся кода, было бы хорошо полностью изменить дизайн вашего класса.

7

Здесь есть несколько вопросов.

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

Как правило, использование макросов плохо. Мы не принимаем макропрограммирование в любом обзоре кода. Проверьте руководство C ++ ISO для получения дополнительной информации:

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

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-macros

5

я бы очень с осторожностью #include это не появилось в :: объем, особенно учитывая, что это конец файла .h,

Я также опасаюсь #defineособенно тогда, когда вы этого не сделаете #undef это после #include,

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

4