假设您有一个功能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对象。
那么,有没有更好的方法来实现这一目标?
答案 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
对象上调用,因此它们可能无法按预期运行。
但是,这也可能是一个优点:如果以这样的方式编写X
和Y
,则默认构造的对象(或使用构造函数中指定的特殊标志构造的对象)表现出来就像一个模拟类,你甚至不需要一个模拟类(只需构造dummy
)。但是,你几乎可以构建那个&#34;禁用&#34;功能进入X
本身并且您不需要显示器......; - )