在 C++ 中存储视图类型的引用变体
Storing an variant of references for a view type in C++
我的环境没有 C++17 (C++14 ATM) 功能也没有提升。
目前我有一个 class 负责在我们域中的服务之间发送消息,这个 class 使用多种类型的寻址(两种类型都非常重要)并且其中一种可以转换为其他(假设 A 可以转换为 B)。
Class 负责发送消息,包含多个(模板化,因为消息没有基础 class)方法,使用函数重载为两种寻址类型复制。
class Sender{
public:
// old API
template<typename TReq>
send(const A&target, TReq req){send(target, make_request{req});}
// old API
template<typename TReq>
send(const B&target, TReq req){sendB(target, make_request{req});}
protected:
// done for mocking/testing purpose only
virtual send(const A&target, MessageProxy message);
virtual sendB(const A&target, MessageProxy message);
};
用户可以互换地址 A 和 B(他们不关心这是 A 还是 B 类型的地址)为了更好地使用 google mock,我需要为它们提供不同的名称重载方法(我可以在模拟中这样做,但决定在这里以不同的方式调用它们,没有区别)
我要解决的问题很简单。提供一个单一的虚拟发送方法,它将采用对地址类型 A 或 B 的引用的“联合”(将其视为 variant),可以与这些方法的模板版本相同的方式使用,例如。
virtual send(const TargetProxy &target, MessageProxy message);
可以用
EXPECT_CALL(mock, send(Eq(A{}), Message{}))...
EXPECT_CALL(mock, send(Eq(B{}), OtherMessage{}))...
携带 A& 或 B& (TargetProxy) 的类型应该包含对 A 或 B 的 const 引用,并且应该仅用于将 A 或 B 从模板化发送传递到虚拟发送,但我没有想出一个简单的方法这个
的解决方案
缺少很多样板文件,但 PoC 可能是这样的:
#include <utility>
#include <cassert>
#include <iostream>
struct A {};
struct B {};
struct EitherRef : private std::pair<A*, B*>
{
EitherRef(A& a)
: std::pair<A*, B*>(std::addressof(a), nullptr){};
EitherRef(B& b)
: std::pair<A*, B*>(nullptr, std::addressof(b)){};
bool hasA() const {return this->first!=nullptr;}
bool hasB() const {return this->second;}
operator A&() { assert (this->first);return *this->first; }
operator B&() { assert (this->second);return *this->second; }
};
template <typename Func>
void applyVisitor(EitherRef e, Func f)
{
if (e.hasA()) {
f(static_cast<A&>(e));
} else if (e.hasB()) {
f(static_cast<B&>(e));
}
}
void f(const EitherRef& r)
{
struct {
void operator()(A&) {
std::cout <<"A\n";
}
void operator()(B&) {
std::cout <<"B\n";
}
} v;
applyVisitor(r, v);
}
int main(int, char*[])
{
A a1;
B b1;
EitherRef ra{a1};
EitherRef rb{b1};
f(ra);
f(rb);
return 0;
}
我的环境没有 C++17 (C++14 ATM) 功能也没有提升。
目前我有一个 class 负责在我们域中的服务之间发送消息,这个 class 使用多种类型的寻址(两种类型都非常重要)并且其中一种可以转换为其他(假设 A 可以转换为 B)。 Class 负责发送消息,包含多个(模板化,因为消息没有基础 class)方法,使用函数重载为两种寻址类型复制。
class Sender{
public:
// old API
template<typename TReq>
send(const A&target, TReq req){send(target, make_request{req});}
// old API
template<typename TReq>
send(const B&target, TReq req){sendB(target, make_request{req});}
protected:
// done for mocking/testing purpose only
virtual send(const A&target, MessageProxy message);
virtual sendB(const A&target, MessageProxy message);
};
用户可以互换地址 A 和 B(他们不关心这是 A 还是 B 类型的地址)为了更好地使用 google mock,我需要为它们提供不同的名称重载方法(我可以在模拟中这样做,但决定在这里以不同的方式调用它们,没有区别)
我要解决的问题很简单。提供一个单一的虚拟发送方法,它将采用对地址类型 A 或 B 的引用的“联合”(将其视为 variant),可以与这些方法的模板版本相同的方式使用,例如。
virtual send(const TargetProxy &target, MessageProxy message);
可以用
EXPECT_CALL(mock, send(Eq(A{}), Message{}))...
EXPECT_CALL(mock, send(Eq(B{}), OtherMessage{}))...
携带 A& 或 B& (TargetProxy) 的类型应该包含对 A 或 B 的 const 引用,并且应该仅用于将 A 或 B 从模板化发送传递到虚拟发送,但我没有想出一个简单的方法这个
的解决方案缺少很多样板文件,但 PoC 可能是这样的:
#include <utility>
#include <cassert>
#include <iostream>
struct A {};
struct B {};
struct EitherRef : private std::pair<A*, B*>
{
EitherRef(A& a)
: std::pair<A*, B*>(std::addressof(a), nullptr){};
EitherRef(B& b)
: std::pair<A*, B*>(nullptr, std::addressof(b)){};
bool hasA() const {return this->first!=nullptr;}
bool hasB() const {return this->second;}
operator A&() { assert (this->first);return *this->first; }
operator B&() { assert (this->second);return *this->second; }
};
template <typename Func>
void applyVisitor(EitherRef e, Func f)
{
if (e.hasA()) {
f(static_cast<A&>(e));
} else if (e.hasB()) {
f(static_cast<B&>(e));
}
}
void f(const EitherRef& r)
{
struct {
void operator()(A&) {
std::cout <<"A\n";
}
void operator()(B&) {
std::cout <<"B\n";
}
} v;
applyVisitor(r, v);
}
int main(int, char*[])
{
A a1;
B b1;
EitherRef ra{a1};
EitherRef rb{b1};
f(ra);
f(rb);
return 0;
}