有没有办法在模板装饰器中使用的静态方法中修改类属性?

huangapple go评论85阅读模式
英文:

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

问题

我正在学习Cpp模板并尝试实现类似装饰器的功能,以便在调用`log.warn`时,在`warn`方法之上执行一些额外的代码,从而在不实际覆盖它的情况下向方法添加额外的行为。

#include <cstdio>

template <typename Func>
struct Decorator {
    Func* func;
    template <typename... Args>
    auto operator()(Args... args) {
        printf("在函数执行之前\n");
        (*func)(args...);
        printf("在函数执行之后\n");   
    }
};

class Logger{
	private:
		int val= 0;
	    static void warn(char * str, char * ptr){
	    	printf("在warn()内部\n");
	    	printf("%s %s\n", str, ptr);
	    }
	    void info(){printf("在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;
}
此类方法的目标是在实际调用`warn`之前打印时间。
我无法弄清楚的另一个目标是如何修改类属性的值,例如静态函数中的`val`,以便每次调用`warn`时都会增加`val`的值。
英文:

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.

答案1

得分: 3

问题在于,为了在Decorator中增加val,您需要在Decorator中有一个可用的实例。这使您可以调用增量函数,您可以提供该函数。

然而,有多种方法可以实现这一点。您在自己发布的答案中提到的方法很繁琐,因为每次都需要将log实例传递给Decorator::operator()

我会通过让Logger成为自己的成员来避免这个问题。以下是一个示例C++20代码:

#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("在函数执行之前\n");
        Func(mInstance, std::forward<Args>(args)...);
        printf("在函数执行之后\n");
    }
};

class Logger 
{
private:
    int val = 0;

    static void warn(Logger* self, char* str, char* ptr) {
        printf("在warn()内部\n");
        printf("%s %s\n", str, ptr);
        self->val++; // 增加类属性
    }

    void info() {
        printf("在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;
}

注意:这是您提供的C++代码的翻译部分。

英文:

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, &amp;log);

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

#include &lt;cstdio&gt;
#include &lt;utility&gt;

template &lt;auto Func, typename Instance&gt;
class Decorator
{
    Instance* mInstance{ nullptr };
public:
    explicit Decorator(Instance* object)
        : mInstance{ object }
    {}

    template &lt;typename... Args&gt;
    auto operator()(Args&amp;&amp;... args)
    {
        printf(&quot;Before function execution\n&quot;);
        Func(mInstance, std::forward&lt;Args&gt;(args)...);
        printf(&quot;After function execution\n&quot;);
    }
};

class Logger 
{
private:
    int val = 0;

    static void warn(Logger* self, char* str, char* ptr) {
        printf(&quot;Inside warn()\n&quot;);
        printf(&quot;%s %s\n&quot;, str, ptr);
        self-&gt;val++; // Increment the class attribute
    }

    void info() {
        printf(&quot;Inside info()\n&quot;);
    }

public:
    Decorator&lt;&amp;Logger::warn, Logger&gt; decoratedWarn{ this };

    int get_val() 
    {
        return val;
    }
};

int main()
{
    char str[6] = &quot;hello&quot;;
    char ptr[6] = &quot;world&quot;;
    Logger log;
    log.decoratedWarn(str, ptr);
    printf(&quot;%d\n&quot;, log.get_val());  // 1

    log.decoratedWarn(str, ptr);
    printf(&quot;%d\n&quot;, log.get_val()); // 2
    return 0;
}

答案2

得分: 2

如463035818_is_not_an_ai所提到的,可以通过将当前对象实例作为方法的参数来实现这一目标,最终结果如下:

class Logger {
private:
    int val = 0;
    static void warn(char* str, char* ptr, Logger* log) {
        printf("Inside warn()\n");
        printf("%s %s\n", str, ptr);
        log->val += 1;
    }
    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, &log);
    printf("%d\n", log.get_val());
    return 0;
}

通过将 warn(char* str, char* ptr) 更改为 warn(char* str, char* ptr, Logger* log) 并将方法调用更改为 log.decoratorWarn(str, ptr, &log) 来实现。

英文:

As mentioned by 463035818_is_not_an_ai this can be achieved by passing the current object instance as an argument to the method the end result would become

class Logger{
	private:
		int val= 0;
	    static void warn(char * str, char * ptr, Logger * log){
	    	printf(&quot;Inside warn()\n&quot;);
	    	printf(&quot;%s %s\n&quot;, str, ptr);
	    	log-&gt;val +=1;
	    }
	    void info(){printf(&quot;Inside info()\n&quot;);}
	public:
		Decorator&lt;decltype(warn)&gt; decoratedWarn{warn};
		int get_val(){return val;}
};

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

by changing warn(char * str, char * ptr) to warn(char * str, char * ptr, Logger * log) and calling the method as log.decoratorWarn(str, ptr, &amp;log).

huangapple
  • 本文由 发表于 2023年7月7日 03:21:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76631960.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定