我正在使用可变参数模板实现观察者模式。
Subject 类有参数作为订阅者,并将它们存储在
std::vector<std::variant>
中。在函数 notify
中,我使用 std::visit
来调用函数 onMessage
来通知所有订阅者。
template<typename...Args>
class SubjectBase {
using ArgsType = std::variant<Args*...>;
std::vector<ArgsType> subscribers_;
public:
SubjectBase(Args*... args): subscribers_{args...} {}
template<typename Msg>
auto notify(const Msg& msg) {
for(auto subscriber: subscribers_) {
std::visit([msg](const auto& x) {
x->onMessage(msg);
}, subscriber);
}
}
private:
};
Subscriber 类具有参数作为它想要接收的消息类型(例如,SubscriberOne 获取消息
Foo
、Bar
,但 SubscriberTwo 只获取消息 Foo
)
struct Foo {};
struct Bar {};
template<typename ...Args>
class SubscriberBase {
public:
auto onMessage(const auto& msg);
};
class SubscriberOne: public SubscriberBase<Foo, Bar> {
public:
auto onMessage(const Foo& foo) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
auto onMessage(const Bar& foo) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
class SubscriberTwo: public SubscriberBase<Foo> {
public:
auto onMessage(const Foo& foo) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
例如我的主要功能。
int main() {
SubscriberOne one;
SubscriberTwo two;
SubjectBase<SubscriberOne, SubscriberTwo> subjectOne(&one, &two);
SubjectBase<SubscriberOne> subjectTwo(&one);
Foo foo;
Bar bar;
subjectOne.notify(foo);
// subjectOne.notify(bar); --> // this line error due to SubcriberTwo has no onMessage(Bar&)
return 0;
}
我们可以检查
std::visit
中的函数是否可调用吗?例如,使用 if constexpr
检查变体中的类型。
我的猜测是你使用的是 C++20。如果是这样,您可以使用概念来检查您想要的内容。 如果您使用一些较旧的标准,这可能也是可行的,但方式有点复杂。
template<typename T, typename MSG>
concept HasMessageHandler = requires(T obj, const MSG& msg) {
{ obj.onMessage(msg) } ;
};
template<typename...Args>
class SubjectBase {
using ArgsType = std::variant<Args*...>;
std::vector<ArgsType> subscribers_;
public:
SubjectBase(Args*... args): subscribers_{args...} {}
template<typename Msg>
auto notify(const Msg& msg) {
for(auto subscriber: subscribers_) {
std::visit([msg](const auto& x) {
using ObjType = std::remove_pointer_t<std::remove_reference_t<decltype(x)>>;
// uncomment line below to see how it fails
// static_assert(HasMessageHandler<ObjType, decltype(msg)>);
if constexpr(HasMessageHandler<ObjType, decltype(msg)> ) {
x->onMessage(msg);
}
}, subscriber);
}
}
private:
};
另外,您心里没有某种 CRTP 吗? 因为目前
SubscriberBase
是完全没有必要的。
演示:
https://godbolt.org/z/87jcsejTb
去掉 SubscriberBase
后:
https://godbolt.org/z/c4cMajzrP