Search code examples
c++wrapperabstraction

Strategy for wrapping multiple libraries in C++


I have a class Foo, which I do not implement directly, but wrap external libraries (e.g FooXternal1 or FooXternal2 ) One way that I have seen to do this, is using preprocessor directives as

#include "config.h"
#include "foo.h"
#ifdef _FOOXTERNAL1_WRAPPER_
//implementation of class Foo using FooXternal1
#endif

#ifdef _FOOXTERNAL2_WRAPPER_
//implementation of class Foo using FooXternal2
#endif

and a config.h is used to define these preprocessor flags (_FOOXTERNAL1_WRAPPER_ and _FOOEXTERNAL2_WRAPPER_). I have the impression this is frowned upon by the C++ programmer community because it uses preprocessor directives, is hard to debug, etc. Further, it does not allow for the parallel existence of both implementations.

I thought about making Foo a base class and inheriting from it to allow for both implementations to exist in parallel with each other. But I ran into two problems:

  1. Pure virtual functions: cannot instatiate an object of type 'Foo', which I need during use.
  2. Virtual functions run the risk of running an object with no (proper) implementation.

Am I missing something? Is there a cleaner way to do this?

EDIT : To summarize, there are 3(.5?!) ways to doing the wrapping- 2(.5) are given by icepack, and the last by Sergey 1- Use factory methods 2- Use preprocessor directives 2.5- Use makefile or IDE to effectively do the work of the preprocessor directives 3.5- Use templates suggested by Sergay

I am working on an embedded system where resources are limited, I decided to use template<enum = default_library>, with template specialization. It is easy to understand for later users; at least thats what I think


Solution

  • If all method names of external implementations are similar, you can use templates. Let external implementations look like:

    class FooX1
    {
    public:
        void Met1()
        {
            std::cout << "X1\n";
        }
    };
    
    class FooX2
    {
    public:
        void Met1()
        {
            std::cout << "X2\n";
        }
    };
    

    Then you can use several variants.

    Variant 1. You can declare member of a template type and wrap all calls to external implementation, even with some preparations before the call. Don't forget to delete impl in ~Foo destructor.

    template<typename FooX>
    class FooVariant1
    {
    public:
        FooVariant1()
        {
            impl=new FooX();
        }
    
        void Met1Wrapper()
        {
            impl->Met1();
        }
    private:
    
        FooX *impl;
    };
    

    Usage:

    FooVariant1<FooX1> bar;
    bar.Met1Wrapper();
    

    Variant 2. You can inherit from a template parameter. In this case you don't declare any members, but just call implementation's methods by their names.

    template<typename FooX>
    class FooVariant2 : public FooX
    {
    };
    

    Usage:

    FooVariant2<FooX1> bar;
    bar.Met1();
    

    A disadvantage of using templates is that there is no easy way to change implementations in runtime. But in return you get much more optimal code, because types are generated in compile-time and there is no table of virtual functions, which can make the program slower.