装饰器的类方法

时间:2016-12-16 16:56:33

标签: c++

假设我有一个测量给定函数持续时间的函数(装饰器):

#include <unistd.h>

void measure(void (*f)()) {
    time_t tBegin = time(NULL);
    f();
    time_t tEnd = time(NULL);
    cout << "Duration: " << (tEnd - tBegin) << " sec" << endl;
}

我想测量一个类方法的持续时间。例如:

class Myclass {
private:
    double _d;

public:
    Myclass(double d) : _d(d) {}

    void run() {
        measure(m);
    }

    void m() const {
        usleep(1000000 * _d);
    }
};

int main() {
    Myclass obj(2.0);
    obj.run();
    return 0;
}

此类实施会导致错误:

error: invalid use of non-static member function

C ++中有没有正确实现它的方法?它应该不修改外部函数measure,并且测量的方法完全是非静态的(它使用实例的数据)。测量应该在方法run内。

我需要C ++ 1998/2003标准的解决方案。

3 个答案:

答案 0 :(得分:2)

  1. measure更改为函数模板,以允许您使用任何可调用函数,而不仅仅是函数。

  2. run中使用lambda函数。

  3. #include <iostream>
    #include <time.h>
    #include <unistd.h>
    
    template <typename F>
    void measure(F f) {
        time_t tBegin = time(NULL);
        f();
        time_t tEnd = time(NULL);
        std::cout << "Duration: " << (tEnd - tBegin) << " sec" << std::endl;
    }
    
    class Myclass {
    private:
        double _d;
    
    public:
        Myclass(double d) : _d(d) {}
    
        void run() {
            measure([=](){m();});
        }
    
        void m() const {
            usleep(1000000 * _d);
        }
    };
    
    int main() {
        Myclass obj(2.0);
        obj.run();
        return 0;
    }
    

    由于您不允许修改measure,因此您可以使用帮助程序类模板和函数模板来使您可以使用任何可调用对象。

    #include <iostream>
    #include <time.h>
    #include <unistd.h>
    
    void measure(void (*f)()) {
        time_t tBegin = time(NULL);
        f();
        time_t tEnd = time(NULL);
        std::cout << "Duration: " << (tEnd - tBegin) << " sec" << std::endl;
    }
    
    template <typename F>
    struct MeasureFunctor
    {
       static F* f_;
       static void run(){(*f_)();}
    };
    
    template <typename F> F* MeasureFunctor<F>::f_ = nullptr;
    
    template <typename F>
    void measure(F f) {
       MeasureFunctor<F>::f_ = &f;
       measure(MeasureFunctor<F>::run);
    }
    
    class Myclass {
    private:
        double _d;
    
    public:
        Myclass(double d) : _d(d) {}
    
        void run() {
            measure([=](){m();});
        }
    
        void m() const {
            usleep(1000000 * _d);
        }
    };
    
    int main() {
        Myclass obj(2.0);
        obj.run();
        return 0;
    }
    

答案 1 :(得分:2)

既然你提到你已经坚持使用C ++ 03,那么答案就是在不改变method的签名的情况下,你会遇到一些static tomfoolery:

我要做的事情的要点是为你的特定用例模仿C ++ 11样式的lambda(用const成员函数调用带有返回{的空参数列表{ {1}})。你可以做一些工作来使这段代码更加通用。

首先我发布包装代码,然后我将其分解:

void

Live Demo

击穿

template<class T, void(T::*PTR)()const, size_t I> struct bind_member { typedef void(*fn_type)(); explicit bind_member(const T* _ptr) { ptr = _ptr; } static void func(void) { (ptr->*PTR)(); } operator fn_type() { return &func; } private: static const T* ptr; }; template<class T, void(T::*PTR)()const, size_t I> const T* bind_member<T, PTR, I>::ptr = NULL; 结构(bind_member)上的模板参数用于

  • template<class T, void(T::*PTR)()const, size_t I>是我们正在包装其成员函数指针的类的类型。这在您的示例中变为T
  • Myclassvoid(T::*PTR)()const上const成员函数指针的丑陋语法。此模板参数的名称为T
  • 包含
  • PTR,以便您可以让size_t I的多个实例使用相同的lambda来包装相同的函数,只需要为每个实例提供自己的唯一ID避免他们踩到对方的脚趾。 (在我们的示例中,我们只有一个实例,所以我给它Myclass而不是制作一些复杂的静态计数器/成员ID组合。)

0中,我们的构造函数接受指向类实例的指针。这仅用于设置bind_member类成员。

接下来我们有static,这是我们称之为func的包装方式。如果Myclass::m接受m,则int也会接受func。再一次,你可以做一些花哨的模板,使这一切更加通用。

现在就是诀窍: 我们提供了bind_member结构到C风格函数指针的隐式转换,我们以

的形式进行转换。
typedef void(*fn_type)();
operator fn_type()
{
    return &func;
}

哪个有效,因为我们的func类型是static所以它不需要类的实例,因此不会显示为成员函数指针。 typedef是为了方便起见,它指定func的类型,即它不接受任何参数并返回void

现在实际调用Myclass

void run() 
{
    bind_member<Myclass, &Myclass::m, 0> b(this);
    measure(b);
}

我提供了如上所述的模板参数来创建bind_member的实例,将this作为参数传递,以便bind_member可以调用传递的函数(m) 。我可以将&Myclass::m作为非类型模板参数传递,因为函数指针是指针类型,它们是整数类型,意味着它们具有地址,因此我们可以使用模板参数来获取这些地址。

最后,我们可以将b传递给我们的measure功能,我们已经完成了。

结论

如果可以的话,升级到C ++ 11(或更高版本),并像这里描述的其他一些答案一样写一个lambda。否则,请求将measure的签名更改为可调用的模板,然后我们可以更轻松地包装Myclass::m。如果所有其他方法都失败了,请使用上述方法。

答案 2 :(得分:0)

我也更喜欢lambda(以及std :: function作为度量的参数),但由于你无法改变它,这个想法怎么样:

  1. m()静态。
  2. 向MyClass添加一个静态成员,该成员接受对当前运行measure的MyClass实例的引用。您必须在每次拨打m()之前设置此项。另外,请考虑线程安全性。
  3. m()中,您可以使用此引用转到_d。或者,您甚至可以将_d的值存储在静态成员var中,以使其在m()中可用。取决于MyClass
  4. 实际需要的内容

    此方法允许一次只测量一次调用。对于多个measure()调用的并行执行,您可以使用线程局部存储作为在步骤2中设置的引用。