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

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

По существу, существует заголовочный файл с определением класса для класса с кучей (около 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;

Также

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

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

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

Ответы [ 4 ]

0 голосов
/ 18 сентября 2018

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

Я также настороженно отношусь к #define, особенно потому, что вы не знаете #undef после #include.

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

0 голосов
/ 18 сентября 2018

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

#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, определенного в одном заголовке в другом.

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

0 голосов
/ 18 сентября 2018

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

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

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

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

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

0 голосов
/ 18 сентября 2018

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

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

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

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

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

...