Как я могу упростить объявления интерфейса в C ++? - PullRequest
3 голосов
/ 01 августа 2010

Мои объявления интерфейса обычно (всегда?) Следуют той же схеме. Вот пример:

class Output
{
public:
    virtual ~Output() { }

    virtual void write( const std::vector<char> &data ) = 0;

protected:
    Output() { }

private:
    Output( const Output &rhs ); // intentionally not implemented
    void operator=( const Output &other );  // intentionally not implemented
};

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

ABSTRACT_BASECLASS_BEGIN(Output)
    virtual void write( const std::vector<char> &data ) = 0;
ABSTRACT_BASECLASS_END(Output)

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

Какой самый простой способ объявить интерфейс в C ++ - прямо на языке. Использование препроцессора приемлемо, но я бы хотел избежать внешних генераторов кода.

Ответы [ 4 ]

3 голосов
/ 01 августа 2010

к сожалению, я не нашел хорошего способа сделать это с помощью всего одного макроса.

IMO, самое простое решение - поместить методы по умолчанию в макрос.Например:

#include <vector>

#define DEFAULT_CLASS_METHODS(C) public: \
        virtual ~C(){}; \
    protected: \
        C(){}; \
    private: \
        inline C(const C& rhs){}; \
        inline void operator=(const C& other){}; 

class Output{
    DEFAULT_CLASS_METHODS(Output)
public:
    virtual void write(const std::vector<char> &data) = 0;
};

Таким образом, вам потребуется только один макрос на определение класса в файле * .h.Вам понадобится дополнительный макрос, чтобы фактически объявить конструктор копирования и оператор присваивания в некотором файле * .cpp, или вы можете сделать встроенный конструктор копирования и операторы присваивания встроенными, что обернет все в один макрос.

Единственная проблема в том, что вам нужно будет дважды ввести имя класса.

Существует менее изящный способ сделать это без повторного ввода имени класса:

#include <vector>

#define BEGIN_INTERFACE(C) class C{ \
    public: \
        virtual ~C(){}; \
    protected: \
        C(){}; \
    private: \
        inline C(const C& rhs){}; \
        inline void operator=(const C& other){}; 

BEGIN_INTERFACE(Output)
public:
    virtual void write(const std::vector<char> &data) = 0;
};

Как вы можете видеть, в этом случае макрос съедает открывающую скобку {, что будет оченьвводит в заблуждение.

3 голосов
/ 01 августа 2010

Рассмотрите возможность использования базового класса:

class NoncopyableBase
{
public:
    NoncopyableBase() { }
    virtual ~NoncopyableBase() { }
private:
    NoncopyableBase(const NoncopyableBase&);
    void operator=(const NoncopyableBase&);
};

Любой класс, производный от этого базового класса, будет недоступен для копирования и будет иметь виртуальный деструктор.

3 голосов
/ 01 августа 2010

Лично я бы удалил объявление конструктора копирования.У вас есть чисто виртуальные функции, поэтому экземпляры этого класса не могут быть созданы путем нарезки в любом случае.Если производный класс не подлежит копированию из-за характера ресурса, который он содержит;он может пометить себя как не подлежащий копированию.

Тогда я бы избавился от защищенного конструктора по умолчанию;он ничего не делает, и вы в любом случае должны быть производными от этого класса, поскольку у вас есть чисто виртуальные функции, поэтому он не предотвращает их использование.

Хотя пометка оператора копирования не позволяет кому-либо делать *pBase1 = *pBase2 (эффективнонет, лично я не уверен, стоит ли пытаться предотвратить это.Поскольку в вашем классе нет элементов данных, в сгенерированном им компиляторе нет никакой опасности, поэтому пользователь может правильно использовать указатели или ссылки на базовые классы.

Я бы просто сказал:

class Output
{
public:
    virtual ~Output() {}
    virtual void write( const std::vector<char> &data ) = 0;
};
2 голосов
/ 01 августа 2010

как насчет чего-то подобного:

ABSTRACT_BASECLASS(Output,
    (virtual void write( const std::vector<char> &data ) = 0;)
    (more methods))

(first)(second)(...) - это последовательность препроцессора форсирования, которая на самом деле является единственным аргументом для макроса: http://www.boost.org/doc/libs/1_43_0/libs/preprocessor/doc/index.html

#define BASECLASS(name, methods) \
...\
BOOST_PP_SEQ_CAT(methods)\  //will concatenate methods declarations
...\
...