gpt4 book ai didi

c++ - 可视化 boost sml 状态机

转载 作者:行者123 更新时间:2023-12-02 10:14:08 55 4
gpt4 key购买 nike

我第一次在 boost sml 中构建了一个更大的状态机,需要一种方法来可视化(例如导出到 graphviz)整个状态机。知道怎么做吗?有没有办法遍历状态机的结构并打印出来?

最佳答案

Disclaimer: I have zero experience with SML (had to find where it lives).


第一次尝试,嗯
但是,尝试使用 visit_current_states接口(interface),我想出了这个......不是很好的实现,它映射出一个(复合)状态机,给定一个事件列表:
#include <boost/sml.hpp>

namespace sml = boost::sml;
namespace aux = sml::aux;

struct e1 {};
struct e2 {};
struct e3 {};
struct e4 {};
struct e5 {};

struct sub {
auto operator()() const {
using namespace sml;
// clang-format off
return make_transition_table(
*"idle"_s + event<e3> = "sub1"_s
, "sub1"_s + event<e4> = X
);
// clang-format on
}
};

struct composite {
auto operator()() const {
using namespace sml;
// clang-format off
return make_transition_table(
*"idle"_s + event<e1> = "s1"_s
, "s1"_s + event<e2> = state<sub>
, state<sub> + event<e5> = X
);
// clang-format on
}
};

#include <boost/hana.hpp>
#include <boost/core/demangle.hpp>
#include <iostream>
#include <iomanip>

namespace mapper {
namespace hana = boost::hana;
using namespace std::string_literals;
using boost::core::demangle;

template <typename F> struct ycombine {
ycombine(F f):f(f) {}
F f;
template <typename... A>
auto operator()(A... a) const { return f(*this, a...); };
};

hana::tuple<e1,e2,e3,e4,e5> events;

template <class TSM> class Vis {
public:
explicit Vis(const TSM& sm, std::string prefix = "") : sm_{ sm }, prefix(prefix) {}

template <class TSub>
void operator()(aux::string<boost::sml::sm<TSub>>) const {
auto subname = aux::get_type_name<TSub>();

Vis nvis(sm_, prefix + '/' + subname);
sm_.template visit_current_states<aux::identity<TSub>>(nvis);
prefix = nvis.prefix;
}

template <class TState> void operator()(TState state) const {
prefix += "/"s + state.c_str();
}

private:
const TSM& sm_;
public:
mutable std::string prefix;
};

template <typename SM>
std::string get_current(SM const& sm) {
Vis<SM> v{sm};
sm.visit_current_states(v);
return v.prefix;
};
}

int main() {
using namespace mapper;

auto recurse = ycombine {
[](auto self, auto sm) {
hana::for_each(events, [=](auto ev) {
auto clone = sm;
auto from = get_current(clone);
clone.process_event(ev);
auto to = get_current(clone);

if (from != to) {
std::cout
<< std::quoted(from) << " -> "
<< std::quoted(to)
<< " [label=" << std::quoted(demangle(typeid(ev).name())) << "]\n";

self(clone);
}
});
} };

std::cout << "digraph {\n";
recurse(sml::sm<composite>{});
std::cout << "}\n";
}
虽然我不知道这有多大用处,但至少我可以用 graphviz 渲染结果:
enter image description here
第二次尝试 - 阅读精细手册 :derp:
看起来有一种更简洁的方法来做这些事情,尤其是嵌套 typedef transitions 的存在阅读 latest examples 时:
// $CXX -std=c++14 plant_uml.cpp
#include <boost/sml.hpp>
#include <cassert>
#include <iostream>
#include <string>
#include <typeinfo>

namespace sml = boost::sml;

struct e1 {};
struct e2 {};
struct e3 {};
struct e4 {};

struct guard {
bool operator()() const { return true; }
} guard;

struct action {
void operator()() {}
} action;

struct plant_uml {
auto operator()() const noexcept {
using namespace sml;
return make_transition_table(
*"idle"_s + event<e1> = "s1"_s
, "s1"_s + event<e2> [ guard ] / action = "s2"_s
, "s2"_s + event<e3> [ guard ] = "s1"_s
, "s2"_s + event<e4> / action = X
);
}
};

template <class T>
void dump_transition() noexcept {
auto src_state = std::string{sml::aux::string<typename T::src_state>{}.c_str()};
auto dst_state = std::string{sml::aux::string<typename T::dst_state>{}.c_str()};
if (dst_state == "X") {
dst_state = "[*]";
}

if (T::initial) {
std::cout << "[*] --> " << src_state << std::endl;
}

std::cout << src_state << " --> " << dst_state;

const auto has_event = !sml::aux::is_same<typename T::event, sml::anonymous>::value;
const auto has_guard = !sml::aux::is_same<typename T::guard, sml::front::always>::value;
const auto has_action = !sml::aux::is_same<typename T::action, sml::front::none>::value;

if (has_event || has_guard || has_action) {
std::cout << " :";
}

if (has_event) {
std::cout << " " << boost::sml::aux::get_type_name<typename T::event>();
}

if (has_guard) {
std::cout << " [" << boost::sml::aux::get_type_name<typename T::guard::type>() << "]";
}

if (has_action) {
std::cout << " / " << boost::sml::aux::get_type_name<typename T::action::type>();
}

std::cout << std::endl;
}

template <template <class...> class T, class... Ts>
void dump_transitions(const T<Ts...>&) noexcept {
int _[]{0, (dump_transition<Ts>(), 0)...};
(void)_;
}

template <class SM>
void dump(const SM&) noexcept {
std::cout << "@startuml" << std::endl << std::endl;
dump_transitions(typename SM::transitions{});
std::cout << std::endl << "@enduml" << std::endl;
}

int main() {
sml::sm<plant_uml> sm;
dump(sm);
}
输出,以获得灵感:
@startuml

[*] --> idle
idle --> s1 : e1
s1 --> s2 : e2 [guard] / action
s2 --> s1 : e3 [guard]
s2 --> terminate : e4 / action

@enduml
显然,不是graphviz,但实际上看起来更“值得信赖”,因为
  • 它类似于某种标准的 UML 表示法,
  • 由图书馆作者撰写?
  • 包括我无法做到的细节(主要是因为不知道它们的存在/工作)
  • 关于c++ - 可视化 boost sml 状态机,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62529818/

    55 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com