检查可从 std::variant 调用的函数

问题描述 投票:0回答:1

我正在使用可变参数模板实现观察者模式。

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++ variadic-templates
1个回答
0
投票

我的猜测是你使用的是 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

© www.soinside.com 2019 - 2024. All rights reserved.