[Boost::ext].SML:使用“_”(下划线)占位符时如何在 "on_entry<_>" 和 "on_exit<_>" 中获取 "real" 事件
[Boost::ext].SML: How to get the "real" event in "on_entry<_>" and "on_exit<_>" when using the '_' (underscore) placeholder
前提条件:
我使用 Compiler Explorer 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{});
}
问题 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 中,您需要用 SFINAE 替换 if constexpr
。
所以代码如下:
// 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)>());
}
注意
- clang 10.0.0 及更高版本适用于选项 1。
问题 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 结构中。
前提条件:
我使用 Compiler Explorer 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{});
}
问题 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 中,您需要用 SFINAE 替换 if constexpr
。
所以代码如下:
// 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)>());
}
注意
- clang 10.0.0 及更高版本适用于选项 1。
问题 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 结构中。