c++macrospimpl-idiom

Pimpl idiom through macro


I try to reduce code duplication in a class which implements the pimpl idiom.

Imagine I have a header Foo.h. For better readability, I have reduced the methods.

class FooImp;

class Foo
{
public:
    void A(int x);
    void B(int x, double b);

private:
    FooImp* _imp;
};

Then I have an implementation file like the following:

class FooImp
{
public:
    void A(int x) {}
    void B(int x, double b) {}
};

class Logger{};
class Measure{};

void Foo::A(int x)
{
    Measure m;
    _imp->A(x);
    Logger log;
}

void Foo::B(int x, double b)
{
    Measure m;
    _imp->B(x, b);
    Logger log;
}

So around the function call, I measure something, log something and so on... The construct is always the same. I would like to have a macro that reduces the code overhead here if possible. I imagine something like this:

#define PIMPL_FUNCTION(x, ...)  \
    void Foo::x                 \
    {                           \
        Measure m;              \
        ptr->x(...);            \  
        Logger log;             \
    }                           \

PIMPL_FUNCTION(A(int x), x);
PIMPL_FUNCTION(B(int x, double b), x, b)    

Of course, this code snippet does not compile. I am unsure if what I am trying is even possible with macros. I am open to any ideas or suggestions.

A little background. All these methods are going to be API-calls for a library and the number of methods is going to increase.


Solution

  • MACRO's are not the first thing you should try in C++. They have a lot of downsides. Instead you should try to use (function) templates if you can.

    Like in this online demo : https://onlinegdb.com/QXbtOnhYeR

    And source code :

    #include <iostream>
    #include <memory>
    
    class FooImp
    {
    public:
        void A(int x)
        {
            std::cout << "FooImp::A(" << x << ")\n";
        }
    
        int B(int x)
        {
            std::cout << "FooImp::B(" << x << ")\n";
            return x * x;
        }
    };
    
    class Foo
    {
    public:
        Foo() : 
            _imp{ std::make_unique<FooImp>() }
        {
        }
    
        void A(int x)
        {
            // use a lambda funtion to call _imp->A
            call_impl([&] { _imp->A(x); });
        }
    
        int B(int x)
        {
            // use a lambda funtion to call _imp->B to show return values work too
            return call_impl([&] { return _imp->B(x); });
        }
    
    private:
        // No MACRO use a function template 
        // see lamdba functions https://en.cppreference.com/w/cpp/language/lambda
        // decltype(fn()) = return type of function fn
        template<typename fn_t>
        auto call_impl(fn_t fn) -> decltype(fn()) // pass a function object (lambda/std::function)
        {
            std::cout << "Construct Measure here\n";
            
    
            if constexpr (std::is_same_v<void, decltype(fn()) >)
            {
                fn();
                std::cout << "Construct Log here\n";
            }
            else
            {
                auto retval = fn();
                std::cout << "Construct Log here\n";
                return retval;
            }
        }
    
        std::unique_ptr<FooImp> _imp; // do NOT use raw pointers
    };
    
    int main()
    {
        Foo foo;
        foo.A(1);
        std::cout << foo.B(2) << "\n";
        return 0;
    }