はじめに
同じコンセプトのC#版のステートマシンは以前に投稿しました.
- 一つのクラス内で閉じている
- ステートクラスを実装しなくてよい
- メンバに自然にアクセスできる
- 階層化は不要
- 使用側のコード量は少なく
メンバ関数ポインタ
マクロでコーディングは省略するので関数でもほぼ同じことができますが, メンバ関数ポインタの方がよりスッキリした実装になると思います.
#ifndef INC_MODE_H_
#define INC_MODE_H_
/**
@file Mode.h
@date 2025/02/11 create
*/
#include <cstdint>
#include <cassert>
#include <type_traits>
namespace mode
{
using s32 = int32_t;
#define MODE_DECL(CLASS_NAME, STATE_TYPE, STATE_NUM, TYPENAME) \
friend class mode::Mode<CLASS_NAME, STATE_TYPE, STATE_NUM>; \
template<STATE_TYPE N> \
void init(); \
template<STATE_TYPE N> \
void proc(); \
template<STATE_TYPE N> \
void term(); \
mode::Mode<CLASS_NAME, STATE_TYPE, STATE_NUM> TYPENAME
#define MODE_DESCLSTATEFUNC(STATE_NAME) \
template<> \
void init<STATE_NAME>(); \
template<> \
void proc<STATE_NAME>(); \
template<> \
void term<STATE_NAME>()
#define MODE_IMPL_INIT(CLASS_NAME, STATE_NAME) template<> \
void CLASS_NAME::init<CLASS_NAME::STATE_NAME>()
#define MODE_IMPL_PROC(CLASS_NAME, STATE_NAME) template<> \
void CLASS_NAME::proc<CLASS_NAME::STATE_NAME>()
#define MODE_IMPL_TERM(CLASS_NAME, STATE_NAME) template<> \
void CLASS_NAME::term<CLASS_NAME::STATE_NAME>()
template<class T, class E, E N>
class Mode
{
public:
using this_type = Mode<T, E, N>;
using parent_type = T;
using state_type = E;
using underlying_type = typename std::underlying_type<E>::type;
static_assert(std::is_enum<E>::value, "E should be enum type.");
static_assert(0 < static_cast<underlying_type>(N), "N should be bigger than zero.");
static constexpr underlying_type Max = static_cast<underlying_type>(N);
explicit Mode(T* parent);
Mode(T* parent, state_type state);
Mode(T* parent, underlying_type state);
~Mode();
state_type get() const;
underlying_type getRaw() const;
inline void initialize(state_type state);
void initialize(underlying_type state);
inline void set(state_type state);
void set(underlying_type state);
void update();
private:
template<class T, class U, s32 N>
struct FuncInitializer : FuncInitializer<T, U, N - 1>
{
FuncInitializer()
{
T::inits_[N] = &U::template init<static_cast<state_type>(N)>;
T::procs_[N] = &U::template proc<static_cast<state_type>(N)>;
T::terms_[N] = &U::template term<static_cast<state_type>(N)>;
}
};
template<class T, class U>
struct FuncInitializer<T, U, 0>
{
FuncInitializer()
{
T::inits_[0] = &U::template init<static_cast<state_type>(static_cast<underlying_type>(0))>;
T::procs_[0] = &U::template proc<static_cast<state_type>(static_cast<underlying_type>(0))>;
T::terms_[0] = &U::template term<static_cast<state_type>(static_cast<underlying_type>(0))>;
}
};
using func_type = void (T::*)();
using FuncInitializerType = FuncInitializer<this_type, T, static_cast<underlying_type>(N) - 1>;
void initImpl();
underlying_type prev_;
underlying_type current_;
T* parent_;
static func_type inits_[static_cast<s32>(N)];
static func_type procs_[static_cast<s32>(N)];
static func_type terms_[static_cast<s32>(N)];
};
template<class T, class E, E N>
typename Mode<T, E, N>::func_type Mode<T, E, N>::inits_[static_cast<s32>(N)];
template<class T, class E, E N>
typename Mode<T, E, N>::func_type Mode<T, E, N>::procs_[static_cast<s32>(N)];
template<class T, class E, E N>
typename Mode<T, E, N>::func_type Mode<T, E, N>::terms_[static_cast<s32>(N)];
template<class T, class E, E N>
Mode<T, E, N>::Mode(T* parent)
: parent_(parent)
{
assert(nullptr != parent_);
FuncInitializerType funcInitializer;
initialize(0);
}
template<class T, class E, E N>
Mode<T, E, N>::Mode(T* parent, state_type state)
: parent_(parent)
{
assert(0 <= state && state < N);
assert(nullptr != parent_);
FuncInitializerType funcInitializer;
initialize(state);
}
template<class T, class E, E N>
Mode<T, E, N>::Mode(T* parent, underlying_type state)
: parent_(parent)
{
assert(0 <= state && state < N);
assert(nullptr != parent_);
FuncInitializerType funcInitializer;
initialize(state);
}
template<class T, class E, E N>
Mode<T, E, N>::~Mode()
{
if (prev_ != current_) {
initImpl();
}
do {
if (0 <= prev_) {
s32 prev = prev_;
prev_ = current_;
(parent_->*terms_[prev])();
}
} while (prev_ != current_);
}
template<class T, class E, E N>
typename Mode<T, E, N>::state_type Mode<T, E, N>::get() const
{
return static_cast<E>(current_);
}
template<class T, class E, E N>
typename Mode<T, E, N>::underlying_type Mode<T, E, N>::getRaw() const
{
return static_cast<underlying_type>(current_);
}
template<class T, class E, E N>
inline void Mode<T, E, N>::initialize(state_type state)
{
initialize(static_cast<underlying_type>(state));
}
template<class T, class E, E N>
void Mode<T, E, N>::initialize(underlying_type state)
{
assert(0 <= state && state < Max);
prev_ = -1;
current_ = state;
}
template<class T, class E, E N>
inline void Mode<T, E, N>::set(state_type state)
{
set(static_cast<underlying_type>(state));
}
template<class T, class E, E N>
void Mode<T, E, N>::set(underlying_type state)
{
assert(0 <= state && state < static_cast<s32>(N));
current_ = state;
}
template<class T, class E, E N>
void Mode<T, E, N>::update()
{
assert(0 <= current_ && current_ < static_cast<s32>(N));
if (prev_ != current_) {
initImpl();
}
(parent_->*procs_[current_])();
}
template<class T, class E, E N>
void Mode<T, E, N>::initImpl()
{
do {
if (0 <= prev_) {
(parent_->*terms_[prev_])();
}
prev_ = current_;
(parent_->*inits_[current_])();
} while (prev_ != current_);
}
} // namespace mode
#endif // INC_MODE_H_
使用方法
#include "Mode.h"
#include <cstdio>
class Object
{
public:
Object();
~Object();
void update();
bool isEnd() const;
private:
enum class State
{
S0,
S1,
End,
Num,
};
MODE_DECL(Object, State, State::Num, mode_);
MODE_DESCLSTATEFUNC(State::S0);
MODE_DESCLSTATEFUNC(State::S1);
MODE_DESCLSTATEFUNC(State::End);
};
Object::Object()
:mode_(this)
{
}
Object::~Object()
{
}
void Object::update()
{
mode_.update();
}
bool Object::isEnd() const
{
return State::End == mode_.get();
}
MODE_IMPL_INIT(Object, State::S0)
{
printf("S0 - Init\n");
}
MODE_IMPL_PROC(Object, State::S0)
{
printf("S0 - Proc\n");
mode_.set(State::S1);
}
MODE_IMPL_TERM(Object, State::S0)
{
printf("S0 - Term\n");
}
MODE_IMPL_INIT(Object, State::S1)
{
printf("S1 - Init\n");
}
MODE_IMPL_PROC(Object, State::S1)
{
printf("S1 - Proc\n");
mode_.set(State::End);
}
MODE_IMPL_TERM(Object, State::S1)
{
printf("S1 - Term\n");
}
MODE_IMPL_INIT(Object, State::End)
{
}
MODE_IMPL_PROC(Object, State::End)
{
}
MODE_IMPL_TERM(Object, State::End)
{
}
int main(void)
{
Object obj;
while(!obj.isEnd()){
obj.update();
}
return 0;
}
まとめ
開始時か, コンパイル時に初期化できたらいいのですが.