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