![](/img/trans.png)
[英]C++ static polymorphism (CRTP) and using typedefs from derived classes
[英]Using CRTP for mocking classes
我正在尝试模拟没有任何虚函数的类。 我读过Curiously_recurring_template_pattern (CRTP)可以帮助实现这一目标。
这是代码。 我正在尝试对功能getMyClassValue
进行单元测试
// file myclass.h
template<typename T>
struct MyClass_t {
int hello() {
return (static_cast<T*>(this))->hello_impl();
}
};
/*
Earlier MyClassImpl was just simple class like
struct MyClassImpl {
int hello() {
return 110;
}
};
// I changed it to below for making it mockable. Using CRTP.
*/
struct MyClassImpl : public MyClass_t<MyClassImpl> {
int hello_impl() {
return 110;
}
};
typedef MyClassImpl *MyClass;
int getMyClassValue(MyClass doc) {
return doc->hello();
}
// file main.cpp
#include <iostream>
/*
int main() {
MyClass myclass = new MyClassImpl();
std::cout << getMyClassValue(myclass);
delete myclass;
return 0;
}
*/
// file test.cpp
struct MyClassImplTest : public MyClass_t<MyClassImplTest>,
public virtual MyClassImpl {
int hello_impl() {
return 2;
}
};
int main() {
auto myclass = new MyClassImplTest();
std::cout << getMyClassValue(myclass);
delete myclass;
return 0;
}
我在控制台上得到110
,而不是2
。 为什么会这样呢? 由于我使用的是指针,因此不应进行切片。
我如何实现嘲讽?
确实,这与切片无关。 实际上,发生的事情非常简单:
getMyClassValue
接受类型为MyClassImpl*
的指针并调用hello
MyClassImpl
的父MyClassImpl
MyClass_t<MyClassImpl>
解析为hello
。 hello
static将指针转换为T*
,即MyClassImpl*
并调用hello_impl
MyClassImpl::hello_impl
返回110 要将呼叫hello
不能解析到MyClass_t<MyClassImplTest>
的母公司MyClassImplTest
因为它呼吁类型的指针MyClassImpl*
而不是与类型的指针MyClassImplTest
。 除非您指定要使用哪个父级,否则尝试在MyClassImplTest*
上打个hello
也将不起作用,因为该调用将是模棱两可的。
使用虚函数实现模拟将很简单。 没有,没有那么多。
MyClass
是指向MyClassImpl
的指针。 呼叫hello
就可以调用MyClass_t<MyClassImpl>::hello
,这施放this
来MyClassImpl*
,因此呼吁MyClassImpl::hello_impl
。
如果您使用的是编译时“多态性”(CRTP),则所有类型在编译时都必须正确。
我相信,使这项工作唯一的方法就是将所有内容制作为模板。 您只需将实现类型作为模板参数/参数传递出去。 getMyClassValue
的示例如下:
template <class T_MyClass>
int getMyClassValue(MyClass_t<T_MyClass> *doc)
{
return doc->hello();
}
struct MyClassImplTest : public MyClass_t<MyClassImplTest>
{
int hello_impl() {
return 2;
}
};
int main()
{
auto myclass = new MyClassImplTest();
std::cout << getMyClassValue(myclass);
delete myclass;
return 0;
}
这将必须适用于所有生产代码和所有测试代码。 通过将所有类型收集到一个“类型上下文”(可以存储每个接口的实现类型)中,可以使其更加可行。 然后,这将被明确传递。
struct ProductionContext
{
typedef MyClassImpl MyClass;
typedef MyClass2Impl MyClass2;
};
struct Mock1Context
{
typedef MyClassImplTest MyClass;
typedef MyClass2Impl MyClass2;
};
struct Mock2Context
{
typedef MyClassImpl MyClass;
typedef MyClass2ImplTest MyClass2;
};
template <class T_Context>
int getMyClassValue(typename T_Context::MyClass *doc)
{
return doc->hello();
}
int main()
{
auto myclass = new MyClassImplTest();
std::cout << getMyClassValue<Mock1Context>(myclass);
delete myclass;
return 0;
}
同样,生产和测试代码的每一位都必须由T_Context
进行模板T_Context
。 为了避免所有内容都只包含标头,您可以使用要使用的所有上下文显式实例化模板。
对我来说,这是令人难以置信的笨拙,但这是一个解决方案。
旁注:请不要将指针隐藏在typedef后面。 typedef MyClassImpl *MyClass;
是纯粹的混淆, 永远不会和我一起通过代码审查。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.