How to ensure that every method of a class calls some other method first?

I have :

class Foo {
public:
void log() { }


void a() {
log();
}


void b() {
log();
}
};

Is there a way that I can have each method of Foo, call log(), but without me having to explicitly type log() as the first line of each function ? I want to do this, so that I can add behaviour to each function without having to go through each function and make sure the call is made, and also so that when I add new functions, the code is automatically added...

Is this even possible ? I can't imagine how to do this with macro's, so not sure where to begin... The only way I have thought of so far, is to add a "pre-build step", so that before compiling I scan the file and edit the source code, but that doesn't seem very intelligent....

EDIT: Just to clarify - I don't want log() to call itself obviously. It doesn't need to be part of the class.

EDIT: I would prefer using methods that would work cross platform, and using only the stl.

8538 次浏览

Use a lambda expression and a higher-order function to avoid repetition and minimize the chance of forgetting to call log:

class Foo
{
private:
void log(const std::string&)
{


}


template <typename TF, typename... TArgs>
void log_and_do(TF&& f, TArgs&&... xs)
{
log(std::forward<TArgs>(xs)...);
std::forward<TF>(f)();
}


public:
void a()
{
log_and_do([this]
{
// `a` implementation...
}, "Foo::a");
}


void b()
{
log_and_do([this]
{
// `b` implementation...
}, "Foo::b");
}
};

The benefit of this approach is that you can change log_and_do instead of changing every function calling log if you decide to alter the logging behavior. You can also pass any number of extra arguments to log. Finally, it should be optimized by the compiler - it will behave as if you had written a call to log manually in every method.


You can use a macro (sigh) to avoid some boilerplate:

#define LOG_METHOD(...) \
__VA_ARGS__ \
{ \
log_and_do([&]


#define LOG_METHOD_END(...) \
, __VA_ARGS__); \
}

Usage:

class Foo
{
private:
void log(const std::string&)
{


}


template <typename TF, typename... TArgs>
void log_and_do(TF&& f, TArgs&&... xs)
{
log(std::forward<TArgs>(xs)...);
std::forward<TF>(f)();
}


public:
LOG_METHOD(void a())
{
// `a` implementation...
}
LOG_METHOD_END("Foo::a");


LOG_METHOD(void b())
{
// `b` implementation...
}
LOG_METHOD_END("Foo::b");
};

I agree on what is written on the comments of your original posts, but if you really need to do this and you don't like to use a C macro,you can add a method to call your methods.

Here is a complete example using C++ 2011 to handle correctly varying function parameters. Tested with GCC and clang

#include <iostream>


class Foo
{
void log() {}
public:
template <typename R, typename... TArgs>
R call(R (Foo::*f)(TArgs...), const TArgs... args) {
this->log();
return (this->*f)(args...);
}


void a() { std::cerr << "A!\n"; }
void b(int i) { std::cerr << "B:" << i << "\n"; }
int c(const char *c, int i ) { std::cerr << "C:" << c << '/' << i << "\n"; return 0; }
};


int main() {
Foo c;


c.call(&Foo::a);
c.call(&Foo::b, 1);
return c.call(&Foo::c, "Hello", 2);
}

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();

Is it possible to avoid the boilerplate?

No.

C++ has very limited code generation abilities, injecting code automatically is not part of them.


Disclaimer: the following is a deep-dive in proxying, with the call of preventing the user from getting their grubby paws on the functions they should not call without bypassing the proxy.

Is it possible to make forgetting to call pre-/post-function harder?

Enforcing delegation through a proxy is... annoying. Specifically, the functions cannot possibly be public or protected, as otherwise the caller can get its grubby hands on them and you may declare forfeit.

One potential solution is thus to declare all functions private, and provide proxies that enforce the logging. Abstracted this, to make this scale across multiple classes, is horrendously boiler-platey, though it is a one-time cost:

template <typename O, typename R, typename... Args>
class Applier {
public:
using Method = R (O::*)(Args...);
constexpr explicit Applier(Method m): mMethod(m) {}


R operator()(O& o, Args... args) const {
o.pre_call();
R result = (o.*mMethod)(std::forward<Args>(args)...);
o.post_call();
return result;
}


private:
Method mMethod;
};


template <typename O, typename... Args>
class Applier<O, void, Args...> {
public:
using Method = void (O::*)(Args...);
constexpr explicit Applier(Method m): mMethod(m) {}


void operator()(O& o, Args... args) const {
o.pre_call();
(o.*mMethod)(std::forward<Args>(args)...);
o.post_call();
}


private:
Method mMethod;
};


template <typename O, typename R, typename... Args>
class ConstApplier {
public:
using Method = R (O::*)(Args...) const;
constexpr explicit ConstApplier(Method m): mMethod(m) {}


R operator()(O const& o, Args... args) const {
o.pre_call();
R result = (o.*mMethod)(std::forward<Args>(args)...);
o.post_call();
return result;
}


private:
Method mMethod;
};


template <typename O, typename... Args>
class ConstApplier<O, void, Args...> {
public:
using Method = void (O::*)(Args...) const;
constexpr explicit ConstApplier(Method m): mMethod(m) {}


void operator()(O const& o, Args... args) const {
o.pre_call();
(o.*mMethod)(std::forward<Args>(args)...);
o.post_call();
}


private:
Method mMethod;
};

Note: I am not looking forward to adding support for volatile, but nobody uses it, right?

Once this first hurdle passed, you can use:

class MyClass {
public:
static const Applier<MyClass, void> a;
static const ConstApplier<MyClass, int, int> b;


void pre_call() const {
std::cout << "before\n";
}


void post_call() const {
std::cout << "after\n";
}


private:
void a_impl() {
std::cout << "a_impl\n";
}


int b_impl(int x) const {
return mMember * x;
}


int mMember = 42;
};


const Applier<MyClass, void> MyClass::a{&MyClass::a_impl};
const ConstApplier<MyClass, int, int> MyClass::b{&MyClass::b_impl};

It's quite the boilerplate, but at least the pattern is clear, and any violation will stick out like a sore thumb. It's also easier to apply post-functions this way, rather than tracking each and every return.

The syntax to call is not exactly that great either:

MyClass c;
MyClass::a(c);
std::cout << MyClass::b(c, 2) << "\n";

It should be possible to do better...


Note that ideally you would want to:

  • use a data-member
  • whose type encode the offset to the class (safely)
  • whose type encode the method to call

A half-way there solution is (half-way because unsafe...):

template <typename O, size_t N, typename M, M Method>
class Applier;


template <typename O, size_t N, typename R, typename... Args, R (O::*Method)(Args...)>
class Applier<O, N, R (O::*)(Args...), Method> {
public:
R operator()(Args... args) {
O& o = *reinterpret_cast<O*>(reinterpret_cast<char*>(this) - N);
o.pre_call();
R result = (o.*Method)(std::forward<Args>(args)...);
o.post_call();
return result;
}
};


template <typename O, size_t N, typename... Args, void (O::*Method)(Args...)>
class Applier<O, N, void (O::*)(Args...), Method> {
public:
void operator()(Args... args) {
O& o = *reinterpret_cast<O*>(reinterpret_cast<char*>(this) - N);
o.pre_call();
(o.*Method)(std::forward<Args>(args)...);
o.post_call();
}
};


template <typename O, size_t N, typename R, typename... Args, R (O::*Method)(Args...) const>
class Applier<O, N, R (O::*)(Args...) const, Method> {
public:
R operator()(Args... args) const {
O const& o = *reinterpret_cast<O const*>(reinterpret_cast<char const*>(this) - N);
o.pre_call();
R result = (o.*Method)(std::forward<Args>(args)...);
o.post_call();
return result;
}
};


template <typename O, size_t N, typename... Args, void (O::*Method)(Args...) const>
class Applier<O, N, void (O::*)(Args...) const, Method> {
public:
void operator()(Args... args) const {
O const& o = *reinterpret_cast<O const*>(reinterpret_cast<char const*>(this) - N);
o.pre_call();
(o.*Method)(std::forward<Args>(args)...);
o.post_call();
}
};

It adds one byte per "method" (because C++ is weird like this), and requires some fairly involved definitions:

class MyClassImpl {
friend class MyClass;
public:
void pre_call() const {
std::cout << "before\n";
}


void post_call() const {
std::cout << "after\n";
}


private:
void a_impl() {
std::cout << "a_impl\n";
}


int b_impl(int x) const {
return mMember * x;
}


int mMember = 42;
};


class MyClass: MyClassImpl {
public:
Applier<MyClassImpl, sizeof(MyClassImpl), void (MyClassImpl::*)(), &MyClassImpl::a_impl> a;
Applier<MyClassImpl, sizeof(MyClassImpl) + sizeof(a), int (MyClassImpl::*)(int) const, &MyClassImpl::b_impl> b;
};

But at least usage is "natural":

int main() {
MyClass c;
c.a();
std::cout << c.b(2) << "\n";
return 0;
}

Personally, to enforce this I would simply use:

class MyClass {
public:
void a() { log(); mImpl.a(); }
int b(int i) const { log(); return mImpl.b(i); }


private:
struct Impl {
public:
void a_impl() {
std::cout << "a_impl\n";
}


int b_impl(int x) const {
return mMember * x;
}
private:
int mMember = 42;
} mImpl;
};

Not exactly extraordinary, but simply isolating the state in MyClass::Impl makes difficult to implement logic in MyClass, which is generally sufficient to ensure that maintainers follow the pattern.