How to ensure that every method of a class calls some other method first?
Thanks to the unusual properties of operator ->
, we can inject code before any member access, at the expense of a slightly bent syntax:
// Nothing special in Foo
struct Foo {
void a() { }
void b() { }
void c() { }
};
struct LoggingFoo : private Foo {
void log() const { }
// Here comes the trick
Foo const *operator -> () const { log(); return this; }
Foo *operator -> () { log(); return this; }
};
Usage looks as follows:
LoggingFoo f;
f->a();
See it live on Coliru
This a minimal (but pretty general) solution to the wrapper problem:
#include <iostream>
#include <memory>
template<typename T, typename C>
class CallProxy {
T* p;
C c{};
public:
CallProxy(T* p) : p{p} {}
T* operator->() { return p; }
};
template<typename T, typename C>
class Wrapper {
std::unique_ptr<T> p;
public:
template<typename... Args>
Wrapper(Args&&... args) : p{std::make_unique<T>(std::forward<Args>(args)...)} {}
CallProxy<T, C> operator->() { return CallProxy<T, C>{p.get()}; }
};
struct PrefixSuffix {
PrefixSuffix() { std::cout << "prefix\n"; }
~PrefixSuffix() { std::cout << "suffix\n"; }
};
struct MyClass {
void foo() { std::cout << "foo\n"; }
};
int main()
{
Wrapper<MyClass, PrefixSuffix> w;
w->foo();
}
Defining a PrefixSuffix
class, with the prefix code inside its constructor and the suffix code inside the destructor is the way to go. Then, you can use the Wrapper
class (using the ->
to access to your original class' member functions) and prefix and suffix code will be executed for every call.
See it live.
Credits to this paper, where I found the solution.
As a side-note: if the class
that has to be wrapped does not have virtual
functions, one could declare the Wrapper::p
member variable not as pointer, but as a plain object, then hacking a bit on the the semantic of Wrapper
's arrow operator; the result is that you would have no more the overhead of dynamic memory allocation.
You may do a wrapper, something like
class Foo {
public:
void a() { /*...*/ }
void b() { /*...*/ }
};
class LogFoo
{
public:
template <typename ... Ts>
LogFoo(Ts&&... args) : foo(std::forward<Ts>(args)...) {}
const Foo* operator ->() const { log(); return &foo;}
Foo* operator ->() { log(); return &foo;}
private:
void log() const {/*...*/}
private:
Foo foo;
};
And then use ->
instead of .
:
LogFoo foo{/* args...*/};
foo->a();
foo->b();