[Boost::ext].SML:如何在使用“_”(下划线)占位符时获取“on_entry<_>”和“on_exit<_>”中的“真实”事件

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

前提条件:
我使用编译器资源管理器https://godbolt.org/运行下面截取的代码。

问题描述:
我尝试在使用通用事件(“_”下划线)处理 on_entry/on_exit 定义时访问事件成员。
这仅适用于编译器资源管理器上提供的最新 gcc 编译器 x86-64 gcc 11.1。对于旧版本,它会抛出错误。

所以我尝试了不同的解决方案来解决这个问题。

在下面的代码片段中,您会发现 3 个不同的版本可用于测试此问题。
1. 和 2. 版本仅适用于最新的 gcc 编译器 x86-64 gcc 11.1
3.版本允许使用尴尬的reinterpret_cast来解决这个问题,我认为这不是一个很好的解决方案。

问题:
A) 我当前的reinterpret_cast 解决方案是安全的解决方案还是您看到一些陷阱? 如果您发现一些陷阱,请解释它们。谢谢!

B) 是否有任何其他解决方案可以在不使用reinterpret_cast 的情况下解决旧版gcc 编译器(如x86-64 gcc 11.1)的此问题? 谢谢!

代码片段:

#include <https://raw.githubusercontent.com/boost-ext/sml/master/include/boost/sml.hpp>
#include <iostream>

namespace sml = boost::sml;
namespace {

struct BaseEvent {
    BaseEvent(int value = 200) : i(value) {}
    int i;
};

struct e1 : BaseEvent {};
struct e2 {};
struct e3 : BaseEvent { e3() : BaseEvent(300){} };

struct OnEntry{
    // Handle BaseEvent's and access members of that event
    void operator()(const auto& event){
        // 1. Version) Works only with compiler x86-64 gcc 11.1
        const int i = event.i;

        // 2. Version) Works only with compiler x86-64 gcc 11.1
        //const int i = (static_cast<const BaseEvent&>(event)).i;

        // 3. Version) Works with all compilers
        //const int i = (reinterpret_cast<const BaseEvent&>(event)).i;

        printf("[OnEntry] %s | BaseEvent related event with value=%d\n", sml::aux::get_type_name<decltype(event)>(), i);
    }

    // Used to handle non BaseEvent's
    void operator()(const e2& event){
        printf("[OnEntry] %s | other events\n", sml::aux::get_type_name<decltype(event)>());
    }
    
    void operator()(const sml::back::initial& event){}
};

struct TestSm {
    auto operator()() const noexcept {
        using namespace sml;
        return make_transition_table(
            *"idle"_s + event<e1> = "s1"_s,
            "s1"_s + event<e2> = "s2"_s,
            "s2"_s + event<e3> = "s3"_s,

            // The on_entry is defined to handle all events,
            // but OnEntry operator should handle the real event e1 provided from process_event.
            // This only works for compiler x86-64 gcc 11.1 for older versions
            // like x86-64 gcc 10.3 it won't work and requires an
            // awkward reinterpret_cast (see definition of OnEntry above).
            "s1"_s  + on_entry<_> / OnEntry(),
            "s2"_s  + on_entry<_> / OnEntry(),
            "s3"_s  + on_entry<_> / OnEntry()
        );
    }
};

}  // namespace

int main() {
    sml::sm<TestSm> sm;
    sm.process_event(e1{});
    sm.process_event(e2{});
    sm.process_event(e3{});
}
c++ boost state-machine boost-sml
2个回答
1
投票

回答问题A

如果删除以下重载,则会发生无效的内存读取。 这是一个陷阱。

// Used to handle non BaseEvent's
void operator()(const e2& event){
    printf("[OnEntry] %s | other events\n", sml::aux::get_type_name<decltype(event)>());
}

回答问题B

C++17

您可以使用

if constexpr
和 type_traits
std::is_base_of_v
元函数来解决问题。

这是解决方案代码:

void operator()(const auto& event){
    if constexpr (std::is_base_of_v<BaseEvent, std::remove_reference_t<decltype(event)>>) {
        const int i = event.i;
        printf("[OnEntry] %s | BaseEvent related event with value=%d\n", sml::aux::get_type_name<decltype(event)>(), i);
    }
}

我认为使用模板而不是

auto
参数类型更简单。 请参阅以下代码:

template <typename T>
void operator()(const T& event){
    if constexpr (std::is_base_of_v<BaseEvent, T>) {
        const int i = event.i;
        printf("[OnEntry] %s | BaseEvent related event with value=%d\n", sml::aux::get_type_name<decltype(event)>(), i);
    }
}

我已经检查了我的解决方案是否适用于 gcc 7.3.0 及更高版本以及 clang 3.9.1 或更高版本。

C++14

在 C++14 中,您需要将

if constexpr
替换为 SFINAE。 所以代码如下:

// Handle BaseEvent's and access members of that event
template <typename T>
std::enable_if_t<std::is_base_of<BaseEvent, T>::value> 
operator()(const T& event){
    const int i = event.i;
    printf("[OnEntry] %s | BaseEvent related event with value=%d\n", sml::aux::get_type_name<decltype(event)>(), i);
}

// Used to handle non BaseEvent's
template <typename T>
std::enable_if_t<!std::is_base_of<BaseEvent, T>::value> 
operator()(const T& event){
    printf("[OnEntry] %s | other events\n", sml::aux::get_type_name<decltype(event)>());
}

注意


0
投票

回答问题B

根据 Takatoshi Kondo 的回答以及 c++14 标准中缺乏使用 if-constexpr 的情况,我找到了另一种与 Takatoshi 非常相似的解决方案。

以下代码片段必须替换原始问题中的整个 OnEntry 结构:

template <bool B>
struct Handle{ };

// Handle's BaseEvent and access members of that event
template<>
struct Handle<true>
{
    void operator()(const BaseEvent&  event)
    {
        const int i = event.i;
        printf("[OnEntry] %s | BaseEvent related event with value=%d\n", sml::aux::get_type_name<decltype(event)>(), i);
    }
};

// Handles non-BaseEvent's
template <>
struct Handle<false>
{
    template<class T>
    void operator()(const T&  event) {
        printf("[OnEntry] %s | non-BaseEvent related event\n", sml::aux::get_type_name<decltype(event)>());
    }
};

struct OnEntry{     
    template <class T>
    void operator()(const T& event){
         Handle<std::is_base_of<BaseEvent, T>::value>()( event );
    }
};

我唯一不满意的是,由于编译器错误消息“错误:非命名空间范围内的显式专业化”,我无法将此显式模板专业化移动到 OnEntry 结构中。

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