79

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.

pushkin
  • 9,575
  • 15
  • 51
  • 95
Rahul Iyer
  • 19,924
  • 21
  • 96
  • 190
  • 1
    It *could* be done with macros, but it's not really anything I recommend (and therefore won't show). The best way IMO is to be explicit about it, and just call the function first thing you do. That will make it easy for future readers (including you) to understand what's going on. – Some programmer dude Feb 03 '17 at 08:37
  • @Someprogrammerdude well thats exactly what I'm trying to avoid - as in, I do not want to explicitly have to add it to each method, as then anytime a new method is added, someone will have to "know" to add a call to log() – Rahul Iyer Feb 03 '17 at 08:38
  • 2
    google C++ aspect programming. I have not used, this is not recommendation, only point worth reading – Jacek Cz Feb 03 '17 at 08:42
  • 1
    Adding the explicit call will be easier than suddenly wondering why `log` is called when the code doesn't seem to actually call it. Hiding such details will make your code very hard to maintain a couple of years down the line, even if it is yourself that comes back to it. – Some programmer dude Feb 03 '17 at 08:43
  • 6
    You can create function `logAndCallFunc()` with one parameter - pointer to function you want to call after `log()`. – Yuriy Ivaskevych Feb 03 '17 at 08:43
  • @Someprogrammerdude thanks for the suggestion, but I feel the discussion is going off topic (maintainability), and distracting from what I am trying to accomplish... – Rahul Iyer Feb 03 '17 at 08:46
  • @YuriyIvaskevych - I feel that would make it really complicated, because then you have to store a pointer to each function somewhere, which is as much work (or even more), than just adding a log() statement at the start of each function. Or did I misunderstand ? – Rahul Iyer Feb 03 '17 at 08:47
  • Maybe you can tell us *why* you want this? What is the problem you are trying to solve? Is it just to trace function calls? Some other problem? – Some programmer dude Feb 03 '17 at 08:47
  • @John Do you have any limitations as to what you can change (i.e. legacy code), or can you do whatever you want as long as you end up with a `Foo` with that behaviour? – Quentin Feb 03 '17 at 08:48
  • And regarding the comment from @YuriyIvaskevych, you don't need to store functions. Just do e.g. `some_object.logAndCallFunc(&Foo::a)` (where `Foo::a` doesn't call `log`, it's handled by `Foo::logAndCallFunc`). Sure it's not really nice but it is a solution. – Some programmer dude Feb 03 '17 at 08:49
  • @Someprogrammerdude - I feel that getting into the 'why' would really go off topic in this case - Lets just say I want to see if it is possible. – Rahul Iyer Feb 03 '17 at 08:49
  • 3
    In that case I think you should [read about the XY problem](http://xyproblem.info/). – Some programmer dude Feb 03 '17 at 08:50
  • @Someprogrammerdude, what about passing parameters ? (if we use Yuri's technique) ? – Rahul Iyer Feb 03 '17 at 08:50
  • @Quentin I don't have any limitations as of now... – Rahul Iyer Feb 03 '17 at 08:50
  • Not a problem with template parameter packs. – Some programmer dude Feb 03 '17 at 08:51

6 Answers6

116

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

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Can you explain a bit please ? This seems to work! That's incredible.. I had no idea you could do that. What are the risks of doing something like this ? – Rahul Iyer Feb 03 '17 at 09:02
  • 7
    @Quentin: this makes the assumption that `log()` is a nullary function, which seems unrealistic to me. I would expect `a()` to log something different than `b()`. – Vittorio Romeo Feb 03 '17 at 09:03
  • @John well, `operator ->`'s intended purpose is to provide syntactic sugar for smart pointers. It's a usual operator overload in itself, *but* takes a member access sequence on its right-hand side instead of an expression, so we can sneak it in cases like yours. [Paolo's answer](http://stackoverflow.com/a/42020451/3233393) relies on the same principle, using on top of that the recursive behaviour of `operator->` with which he can sneak in a whole temporary object that does work at construction and destruction. – Quentin Feb 03 '17 at 09:09
  • 4
    @VittorioRomeo it does -- depends on OP's actual need, but the question *is* specified like that :) / Off-topic: I loved your black-magic autothreading ECS framework, and the presentation you gave of it :D – Quentin Feb 03 '17 at 09:15
  • 1
    I am both fascinated and horrified by this answer. Then again, this feeling applies to pretty much everything in C(++) – xDaizu Feb 03 '17 at 11:16
  • 6
    @xDaizu aaah, the ancestral clan wars between JS and C++, each finding the other one's syntax terrifying :) – Quentin Feb 03 '17 at 11:24
  • 11
    @Quentin hahaha... it actually comes from waaaay back before I learned JS. It's not the syntax, I like the syntax, [it's the low level operations and obscure overriding](http://stackoverflow.com/a/236770/4209853). I still wake up screaming some nights, when I dream of my first year in university and its pointers, pointers to functions, templates, operator overloads, functions with 12 2-letter parameters (provided like that by the teacher), compilation errors, stacks overflows, memory violations aand... excuse me a moment, I'm gonna have seizure :) – xDaizu Feb 03 '17 at 11:31
  • @Quentin what if the object is of pointer type or dynamically created: `Foo* f = new Foo(); f->a();`? ==> as `this` is implicitly used when referring to class members, `operator->` would also log something each time you use your member: `void Foo::func() { m_fooMember = 1; }`. This would result in messy logs. Am I right? Also, a good idea is to have `log()` function that does not throw – andrgolubev Feb 03 '17 at 13:53
  • @andrgolubev I don't quite understand what you mean. `operator ->` won't be called in `fooMember = 1;`, it's only turning expressions of the form `lf->fooMember` into `lf.operator -> ()->fooMember` (with `lf` being a `LoggingFoo`). – Quentin Feb 03 '17 at 14:03
  • **Doesn't work**: `Foo* foo = proxy.operator->(); foo->a(); foo->b(); foo->c();` will log only once. If you allow `Foo*` to escape your control, you have already lost. – Matthieu M. Feb 03 '17 at 19:26
  • @Quentin sorry for the bad styling. 1) What if your object is dynamically allocated - will `operator->()` be affected somehow? 2) When you call non-static member function of a class: `f.foo()`, the compiler provides your object as a first argument to that function (at least, I assume it does so): `foo(f)` and if inside `foo()` you refer to members: `foo() { m_memberOfFoo = //do smth... }` then it will result into `foo(f) { f->m_memberOfFoo = //do smth... }` (or just simply `this->m_member...` - not sure) which means that for any member _"invocation"_ your log operation will be invoked - right? – andrgolubev Feb 03 '17 at 19:40
  • 9
    @MatthieuM. guard against Murphy, not Machiavelli. – Quentin Feb 03 '17 at 20:04
  • @andrgolubev It won't really be affected, but you'll have to bear the `(*f)->foo()` syntax until you use a reference. 2) No. this operator is only called with the exact syntax `lf->fooMember`, with the arrow. This is really an overload of the operator `->` itself, not of the access action. – Quentin Feb 03 '17 at 20:07
  • Note that you're sacrificing the ability to take a pointer to the member function. In practice I suspect this will prove to not be worth it. – user541686 Feb 05 '17 at 04:14
  • Can one do the same thing with the . operator, so that if we use "." instead of -> log is still called ? – Rahul Iyer Jul 02 '20 at 10:01
  • 1
    @KaizerSozay nope: unfortunately `.` is not an overloadable operator (yet?) – Quentin Jul 02 '20 at 10:06
37

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.

Paolo M
  • 12,403
  • 6
  • 52
  • 73
  • 1
    @John Well, they both looks good to me. I think Quentin gave a more specific (but far _shorter_) answer; mine addresses the problem in a more general way, resulting _longer_, though. – Paolo M Feb 03 '17 at 09:09
  • 1
    From Stroustrup's paper: "I briefly adopted a variant of that idea for C++'s direct ancestor C with Classes. There, one could define a function that would implicitly be called before every call of every member function (except the constructor) and another that would be implicitly called before every return from every member function (except the destructor). The functions providing this prefix/suffix semantics were called `call()` and `return()`. [...] This proposal died – after some experimental use – because of complexities of handling argument and return types, and because it was intrusive" – Paolo M Feb 03 '17 at 09:14
  • 4
    @John Well, mine it's not better... It just handle the issue in a _deeper_ way... I mean: now you have the problem of executing some prefix code to your member functions; tomorrow, you may have the problem of executing subfix code. Let's say I'm looking a bit forward ;) – Paolo M Feb 03 '17 at 09:18
  • 2
    @John You can have a single `w->foo();` call `logBefore()`, then `foo()`, then `logAfter()` in sequence, the latter of which my solution does not do. Do note the caveat that, since it relies on a temporarie's lifetime, the statement `bar(w->foo());` will call `logBefore()`, `foo()`, `bar()` **then** `logAfter()`. – Quentin Feb 03 '17 at 09:18
  • @John example names :) -- `logBefore()` and `logAfter()` are what you'd put in `PrefixSuffix`'s constructor and destructor to be called before and after each function of `Foo`. `bar` is an arbitrary function, which could be intuitively expected to execute *after* `logAfter` -- the use of the temporary makes it otherwise. – Quentin Feb 03 '17 at 09:55
  • **Doesn't work.**: `MyClass c = w.operator->(); c->foo(); c->foo();` will only log once despite the two calls. If you allow `MyClass*` to escape you have already lost. – Matthieu M. Feb 03 '17 at 19:28
18

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();
Jarod42
  • 203,559
  • 14
  • 181
  • 302
9

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");
};
user703016
  • 37,307
  • 8
  • 87
  • 112
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 3
    But then you still have to write "Log_and_do...." to each function, which is as much work as just calling log() in the first place..... The challenge is to insert the function call into each function without having to actually type out "log()" at the start of each function... – Rahul Iyer Feb 03 '17 at 08:52
  • @John: unfortunately there isn't any good way of "injecting" code into existing functions. A macro could help, updating my answer... – Vittorio Romeo Feb 03 '17 at 08:54
  • 2
    But that works out to the same as manually typing log() at the start of each method right ? So we aren't avoiding any boilerplate... the challenge is how to "inject" code (as you put it), using macros or some other technique, so that we avoid having to "remember" to add the log() call to each method, or have an outsider "remember" to call some other function with a pointer to the function we really want to call... – Rahul Iyer Feb 03 '17 at 08:59
  • @John: the point is that there is no way of "injecting" code, not even using macros. You're either gonna need some boilerplate during method definition, or use an outsider `call(...)` function. – Vittorio Romeo Feb 03 '17 at 09:01
9

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);
}
gabry
  • 1,370
  • 11
  • 26
  • 1
    The problem with this is, outsiders calling public methods of foo, will have to know to call "call", instead of "a" or "b" directly... – Rahul Iyer Feb 03 '17 at 08:56
  • If you know how to do this with a C-macro, I would like to know - I'm not sure how I would do this with a macro... – Rahul Iyer Feb 03 '17 at 08:56
  • @John: make `call()` public and `a(), b()` private. In this way outsiders know only one function to invoke i.e `call()`. – sameerkn Feb 03 '17 at 09:01
  • Your syntax is a bit broken -- `a` should be in place of `val`, and the MFP call should look like `(this->*a)();`. – Quentin Feb 03 '17 at 09:11
  • 1
    @sameerkn if outsiders don't know about a() and b(), then how will they pass call() a pointer to them.... – Rahul Iyer Feb 03 '17 at 09:18
  • @gabry: I tried creating various signatures `void a();, void b(int), int c()...` and got some error. You can tidy up the answer by including inside the class just the function `template R call(R (Foo::*f)(TArgs...), const TArgs... args) { this->log(); return (this->*f)(args...); }` – sameerkn Feb 03 '17 at 10:13
5

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.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722