Search code examples
c++templatesdecorator

Is there a way to modify class attributes in static methods used in template decorator?


I am studying Cpp templates and experimenting how to implement a decorator like functionality so that when I call log.warn some extra code is executed on top off the warn method, thus resulting in an extra behavior added to the method without actually overriding it.

#include <cstdio>

template <typename Func>
struct Decorator {
    Func* func;
    template <typename... Args>
    auto operator()(Args... args) {
        printf("Before function execution\n");
        (*func)(args...);
        printf("After function execution\n");   
    }
};

class Logger{
    private:
        int val= 0;
        static void warn(char * str, char * ptr){
            printf("Inside warn()\n");
            printf("%s %s\n", str, ptr);
        }
        void info(){printf("Inside info()\n");}
    public:
        Decorator<decltype(warn)> decoratedWarn{warn};
        int get_val(){return val;}
};

int main(){
    char str[6] = "hello";
    char ptr[6] = "world";
    Logger log;
    log.decoratedWarn(str, ptr);
    printf("%d\n", log.get_val());
    return 0;
}

The goal behind such approach is to print the time before warn is actually called. The other goal which I can't seem to figure out is how to modify the values of the class attributes such as val in the static function so that val is incremented every time warn is called.


Solution

  • The issue is that, in order to increment the val in Decorator, you need an instance in Decorator availabe. This enables you calling the increment function which you may provide.

    However, there are multiple ways to achive this. The one in your own answer that you posted is tedious, becuase it requres each time the log instance to be passed to Decorator::operator().

    log.decoratedWarn(str, ptr, &log);
    

    I would have avoided it by, letting the Logger be houskeeping own members. Follwoing is an example C++20 code:

    #include <cstdio>
    #include <utility>
    
    template <auto Func, typename Instance>
    class Decorator
    {
        Instance* mInstance{ nullptr };
    public:
        explicit Decorator(Instance* object)
            : mInstance{ object }
        {}
    
        template <typename... Args>
        auto operator()(Args&&... args)
        {
            printf("Before function execution\n");
            Func(mInstance, std::forward<Args>(args)...);
            printf("After function execution\n");
        }
    };
    
    class Logger 
    {
    private:
        int val = 0;
    
        static void warn(Logger* self, char* str, char* ptr) {
            printf("Inside warn()\n");
            printf("%s %s\n", str, ptr);
            self->val++; // Increment the class attribute
        }
    
        void info() {
            printf("Inside info()\n");
        }
    
    public:
        Decorator<&Logger::warn, Logger> decoratedWarn{ this };
    
        int get_val() 
        {
            return val;
        }
    };
    
    int main()
    {
        char str[6] = "hello";
        char ptr[6] = "world";
        Logger log;
        log.decoratedWarn(str, ptr);
        printf("%d\n", log.get_val());  // 1
    
        log.decoratedWarn(str, ptr);
        printf("%d\n", log.get_val()); // 2
        return 0;
    }