Hiding multiple implementations behind one interface

I am aware of Strategy Patterns and Abstract Factory Patterns - however they do not solve my current problem:

I am creating a C ++ library that offers a very simple GUI. However, I want the user to be able to choose at compile time which graphics library to use (like Qt or FLTK) to render the GUI. However, the user only needs to know about the methods in my library.

It should be possible to compile the same code without any modifications using either the Qt backend or the FLTK backend.

I thought of something like:

class A
{
  // do things that are not specific to QT or FLTK here as there are many 
  // methods I will need independent of the backend
}

class QT_A : public A
{
  // Implement the actual creation of a window, display of a widget here using Qt
}

class FLTK_A : public A
{
  // Implement the actual creation of a window, display of a widget here using FLTK
}

      

The problem is, I don't want the user to know about QT_A

or FLTK_A

. The user (developer) has to deal with A

. Also, I can't have both options at the same time, since I don't want my library to depend on both Qt and FLTK; only what was selected at compile time.

+3


source to share


3 answers


One option is the Pimpl idiom described in another answer.

Another option is a factory that returns a pointer to the interface class:



std::unique_ptr<A> make_A()
{
#if defined(USING_QT)
    return std::unique_ptr<A>(new QT_A(...));
#elif defined(USING_FLTK)
    return std::unique_ptr<A>(new FLTK_A(...));
#else
    #error "No GUI library chosen"
#endif
}

      

+3


source


The Pimpl idiom can be an alternative. This allows you to create a common interface without structure-dependent members.

class A
{
  struct impl;
  std::unique_ptr<impl> pimpl; // or scoped_ptr/auto_ptr on non-C++11
public:
  A();
  ~A();
 void do_sth();
};

      



The source file can then provide different impl implementations depending on the backend.

#ifdef QT
struct A::impl : QWidget { // Make it polymorphic, if you need
  QImage img;
  QString data;
};

void A::do_sth()
{
  impl->make_it(); // full access to the Qt things
}

A::A()
  : pimpl(new ...)
{
}

A::~A() {} // no need for delete thanks the smart pointer

#endif

      

+4




No need for fancy templates.

You distribute

  • headings A;
  • a library containing functions A

    , QT_A

    and make_A

    ;
  • another library containing A

    , FLTK_A

    and another implementation of the function make_A

    .

The user links to any of the libraries.

+3


source







All Articles