简体   繁体   中英

How can I simplify interface declarations in C++?

My interface declarations usually (always?) follow the same scheme. Here's an example:

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
};

The boilerplate is always the same: public virtual destructor, a few pure virtual methods which make up the actual interface. protected default ctor, disabled copy construction and copy assignment. I started using two little helper macros which can be used to simplify the above to

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

Unfortunately, I didn't find a nice way to do this with just a single macro. Even better, I'd like to avoid macros entirely. However, the only thing which came to my mind was a code generator, which is a bit overkill for me.

What is the simplest way to declare an interface in C++ - directly in the language. Preprocessor use is acceptable, but I'd like to avoid external code generators.

Consider using a base class:

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

Any class derived from this base class will be noncopyable and will have a virtual destructor.

Personally, I would remove the copy constructor declaration. You have pure virtual functions so instances of this class can't be created by slicing in any case. If a derived class isn't copyable because of the nature of a resource that it holds; it can mark itself as non-copyable.

Then I would get rid of the protected default constructor; it doesn't do anything and you have to derive from this class in any case as you have pure virtual functions so it's not preventing any usage.

Although marking the copy assignment operator prevents someone doing *pBase1 = *pBase2 (effectively a no-op), personally I'm not convinced that it's worth the bother of trying to prevent it. As your class has no data members there's no inherent danger in the compiler generated one, it's really up to user to use pointers or references to base classes appropriately.

I would just go with:

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

unfortunately, I didn't find a nice way to do this with just a single macro.

IMO, the easiest solution is to put default methods into macro. Like this:

#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;
};

This way you'll need only one macro per class definition in *.h file. You will need additional macro to actually declare copy constructor and assignment operator in some *.cpp file or you could make default copy constructor and assignment operators inline, which will wrap everything into single macro.

The only problem is that you'll need to type class name twice.

There is a less elegant way to do that without typing class name twice:

#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;
};

As you can see, in this case macro eats opening { bracket, which will be very misleading.

how about something of this sort:

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

(first)(second)(...) is a boost preprocessor sequence, which is really single argument as far as your macro is concerned: 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
...\

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM