条件成员函数执行

时间:2014-10-13 15:32:54

标签: c++

假设您有一个功能Foo的课程Foo::bar()。 这个函数的周围是Monitor<Foo>类,它包含Foo并通过重载operator->转发任何函数调用。

此外,Monitor类有一个布尔标志execute。如果execute为真,则所有函数调用 Foo应该正常执行,但是如果execute设置为false,则应该跳过执行。

以下代码段显示了这种情况:

#include <iostream>

using namespace std;

class Foo {
  void bar() {std::cout << "Foo::bar()";}
};

template<typename T> class Monitor<T> {
  T& ref;
  bool exec;
public:
  Monitor(T& obj) : ref(obj), exec(true) {}
  T* operator->() {/* if exec */ return &ref;}
  void setExec(bool e) {exec = e;}
};

int main() {
  Foo foo;
  Monitor<Foo> monitor(foo);

  monitor->bar(); // call Foo::bar();
  monitor.setExec(false);
  monitor->bar(); // do nothing
}

这可以实现吗?显而易见的解决方案是拥有一个基类IFoo,和 模拟实现MockFoo什么也不做,然后返回指向MockFoo对象的指针 调用operator->时然而,这使得整个事情变得相当不灵活,因为你必须这样做 为您要监视的任何类提供Mock对象。

那么,有没有更好的方法来实现这一目标?

1 个答案:

答案 0 :(得分:1)

如果您知道要打电话的功能,可以执行以下操作。这甚至允许在exec==false的情况下指定函数的默认返回值。我确信我没有考虑引用返回参数,const成员函数等所有可能的陷阱。但我相信如果你想使用它,你可以调整它。

#include <iostream>

struct X {
    double callX(const int& x){ return x/100.;};
};

struct Y {
    int callY(const std::string& y){ return y.length();};
};

template<typename F> class Monitor;

template<typename T, typename Ret, typename ...Args>
class Monitor<Ret(T::*)(Args...)> {
    T& ref;
    Ret(T::*func)(Args...);
    Ret defaultRet;
    bool exec;
public:
    Monitor(T& ref, Ret(T::*func)(Args...), Ret defaultRet = Ret())
        : ref(ref),
          func(func),
          defaultRet(defaultRet),
          exec(true){};
    void setExec(bool e) {exec = e;};
    Ret call(Args&&... args) {
        if(exec)
            return (ref.*func)(std::forward<Args>(args)...);
        else
            return defaultRet;
    };

};

template<typename T, typename Ret, typename ...Args>
auto makeMonitor(T& x, Ret(T::*f)(Args...), Ret r = Ret()) {
    return Monitor<Ret(T::*)(Args...)>(x,f,r);
}


int main() {
    X x;
    Y y;
    auto xmon = makeMonitor(x, &X::callX);
    auto ymon = makeMonitor(y, &Y::callY);
    auto ymon_def = makeMonitor(y, &Y::callY, 123);
    std::cout << "callX(3)=" << xmon.call(3) << std::endl;
    std::cout << "callY(\"hello\")=" << ymon.call("hello") << std::endl;
    std::cout << "[default return] callY(\"hello\")=" << ymon_def.call("hello") << std::endl;
    xmon.setExec(false);
    ymon.setExec(false);
    ymon_def.setExec(false);
    std::cout << "After setExec(false):" << std::endl;
    std::cout << "callX(3)=" << xmon.call(3) << std::endl;
    std::cout << "callY(\"hello\")=" << ymon.call("hello") << std::endl;
    std::cout << "[default return] callY(\"hello\")=" << ymon_def.call("hello") << std::endl;
    return 0;
}

输出是:

callX(3)=0.03
callY("hello")=5
[default return] callY("hello")=5
After setExec(false):
callX(3)=0
callY("hello")=0
[default return] callY("hello")=123

工作示例是here

&#34;显而易见&#34;你提到的解决方案可以简化一点,所以你只需要定义一个额外的(模拟)类而不需要额外的基类。如果您不介意虚拟成员函数导致的轻微性能损失,您可以这样做:

#include <iostream>

struct MockX;

struct X {
    typedef MockX mock;
    virtual double doX(int x){ return x/100.;};
};

struct MockX : X {
    virtual double doX(int x){ return 0.;};
};

struct MockY;

struct Y {
    typedef MockY mock;
    virtual int doY(std::string y){ return y.length();};
};

struct MockY : Y {
    virtual int doY(std::string y){ return 123;};
};


template <typename T>
struct Monitor {
    T& ref;
    static typename T::mock dummy;
    bool exec;
    Monitor(T& ref) : ref(ref), exec(true){};
    void setExec(bool e){exec = e;};
    T* operator->(){
        if(exec)
            return &ref;
        else
            return &dummy;
    };
};

template<typename T>
typename T::mock Monitor<T>::dummy{};

int main() {
    X x;
    Y y;
    auto xmon = Monitor<X>(x);
    auto ymon = Monitor<Y>(y);
    std::cout << "doX(3)=" << xmon->doX(3) << std::endl;
    std::cout << "doY(\"hello\")=" << ymon->doY("hello") << std::endl;
    xmon.setExec(false);
    ymon.setExec(false);
    std::cout << "After setExec(false):" << std::endl;
    std::cout << "doX(3)=" << xmon->doX(3) << std::endl;
    std::cout << "doY(\"hello\")=" << ymon->doY("hello") << std::endl;
    return 0;
}

我制作了dummy模拟对象static,因此您监控的每种类型只会有一个副本。您需要的一切是真实类中的typedef,用于指定您的模拟类,以及继承自真实类的模拟类,并覆盖您在exec==false时要禁用的(虚拟)方法。您必须注意,即使您不会覆盖的方法也会在dummy时在exec==false对象上调用,因此它们可能无法按预期运行。

但是,这也可能是一个优点:如果以这样的方式编写XY,则默认构造的对象(或使用构造函数中指定的特殊标志构造的对象)表现出来就像一个模拟类,你甚至不需要一个模拟类(只需构造dummy)。但是,你几乎可以构建那个&#34;禁用&#34;功能进入X本身并且您不需要显示器......; - )