reverted back to tuples to store fsm state_list

This commit is contained in:
Francisco Paisana 2020-04-05 18:29:24 +01:00 committed by Francisco Paisana
parent dbbeb1ec3b
commit 6e34dadce2
5 changed files with 491 additions and 357 deletions

View File

@ -19,57 +19,13 @@
*
*/
#include <limits>
#include <memory>
#include <type_traits>
#include "type_utils.h"
#ifndef SRSLTE_CHOICE_TYPE_H
#define SRSLTE_CHOICE_TYPE_H
namespace srslte {
namespace choice_details {
using size_idx_t = std::size_t;
static constexpr size_idx_t invalid_idx = std::numeric_limits<size_idx_t>::max();
class bad_choice_access : public std::runtime_error
{
public:
explicit bad_choice_access(const std::string& what_arg) : runtime_error(what_arg) {}
explicit bad_choice_access(const char* what_arg) : runtime_error(what_arg) {}
};
//! Get Index of a type in a list of types (reversed)
template <typename T, typename... Types>
struct type_indexer;
template <typename T, typename First, typename... Types>
struct type_indexer<T, First, Types...> {
static constexpr size_idx_t index =
std::is_same<T, First>::value ? sizeof...(Types) : type_indexer<T, Types...>::index;
};
template <typename T>
struct type_indexer<T> {
static constexpr size_idx_t index = invalid_idx;
};
//! Get a type of an index in a list of types
template <std::size_t I, typename... Types>
struct type_get;
template <std::size_t I, typename First, typename... Types>
struct type_get<I, First, Types...> {
using type = typename std::conditional<I == sizeof...(Types), First, typename type_get<I, Types...>::type>::type;
};
template <std::size_t I>
struct type_get<I> {
using type = void;
};
//! Compute maximum at compile time
template <std::size_t arg1, std::size_t... others>
struct static_max;
@ -83,6 +39,8 @@ struct static_max<arg1, arg2, others...> {
arg1 >= arg2 ? static_max<arg1, others...>::value : static_max<arg2, others...>::value;
};
namespace choice_details {
//! Holds one of the Args types
template <std::size_t MaxSize, std::size_t MaxAlign>
struct choice_storage_t {
@ -94,13 +52,13 @@ struct choice_storage_t {
void* get_buffer() { return &buffer; }
template <typename T>
T& get_unsafe()
T& get_unchecked()
{
return *reinterpret_cast<T*>(&buffer);
}
template <typename T>
const T& get_unsafe() const
const T& get_unchecked() const
{
return *reinterpret_cast<const T*>(&buffer);
}
@ -108,7 +66,7 @@ struct choice_storage_t {
template <typename U>
void destroy_unsafe()
{
get_unsafe<U>().~U();
get_unchecked<U>().~U();
};
};
@ -149,75 +107,29 @@ struct DtorUnsafeVisitor {
C* c;
};
/**
* @brief visit pattern implementation
* @tparam F functor
* @tparam V tagged union type
* @tparam Types remaining types to iterate
*/
template <typename F, typename V, typename... Types>
struct visit_impl;
template <typename F, typename V, typename First, typename... Types>
struct visit_impl<F, V, First, Types...> {
static void apply(V& c, F&& f)
{
if (c.template is<First>()) {
f(c.template get_unsafe<First>());
} else {
visit_impl<F, V, Types...>::apply(c, std::forward<F>(f));
}
}
static void apply(const V& c, F&& f)
{
if (c.template is<First>()) {
f(c.template get_unsafe<First>());
} else {
visit_impl<F, V, Types...>::apply(c, std::forward<F>(f));
}
}
};
template <typename F, typename V, typename T>
struct visit_impl<F, V, T> {
static void apply(V& c, F&& f) { f(c.template get_unsafe<T>()); }
static void apply(const V& c, F&& f) { f(c.template get_unsafe<T>()); }
};
template <typename... Args>
struct tagged_union_t;
template <typename Functor, typename First, typename... Args>
void visit(tagged_union_t<First, Args...>& u, Functor&& f)
{
visit_impl<Functor, tagged_union_t<First, Args...>, First, Args...>::apply(u, std::forward<Functor>(f));
}
template <typename Functor, typename First, typename... Args>
void visit(const tagged_union_t<First, Args...>& u, Functor&& f)
{
visit_impl<Functor, tagged_union_t<First, Args...>, First, Args...>::apply(u, std::forward<Functor>(f));
}
template <typename... Args>
struct tagged_union_t
: private choice_storage_t<static_max<sizeof(Args)...>::value, static_max<alignof(Args)...>::value> {
using base_t = choice_storage_t<static_max<sizeof(Args)...>::value, static_max<alignof(Args)...>::value>;
using buffer_t = typename base_t::buffer_t;
using this_type = tagged_union_t<Args...>;
using default_type = typename type_get<sizeof...(Args) - 1, Args...>::type;
using base_t = choice_storage_t<static_max<sizeof(Args)...>::value, static_max<alignof(Args)...>::value>;
using buffer_t = typename base_t::buffer_t;
using this_type = tagged_union_t<Args...>;
std::size_t type_id;
using base_t::destroy_unsafe;
using base_t::get_buffer;
using base_t::get_unsafe;
using base_t::get_unchecked;
template <typename U, typename... Args2>
void construct_emplace_unsafe(Args2&&... args)
{
using U2 = typename std::decay<U>::type;
static_assert(type_indexer<U2, Args...>::index != invalid_idx,
static_assert(type_list_contains<U2, Args...>(),
"The provided type to ctor is not part of the list of possible types");
type_id = type_indexer<U2, Args...>::index;
type_id = get_type_index<U2, Args...>();
new (get_buffer()) U2(std::forward<Args2>(args)...);
}
@ -225,82 +137,30 @@ struct tagged_union_t
void construct_unsafe(U&& u)
{
using U2 = typename std::decay<U>::type;
static_assert(type_indexer<U2, Args...>::index != invalid_idx,
static_assert(type_list_contains<U2, Args...>(),
"The provided type to ctor is not part of the list of possible types");
type_id = type_indexer<U2, Args...>::index;
type_id = get_type_index<U2, Args...>();
new (get_buffer()) U2(std::forward<U>(u));
}
void copy_unsafe(const this_type& other) { visit(other, CopyCtorVisitor<this_type>{this}); }
void copy_unsafe(const this_type& other) { visit(CopyCtorVisitor<this_type>{this}, other); }
void move_unsafe(this_type&& other) { visit(other, MoveCtorVisitor<this_type>{this}); }
void move_unsafe(this_type&& other) { visit(MoveCtorVisitor<this_type>{this}, other); }
void dtor_unsafe() { visit(*this, choice_details::DtorUnsafeVisitor<base_t>{this}); }
void dtor_unsafe() { visit(choice_details::DtorUnsafeVisitor<base_t>{this}, *this); }
template <std::size_t I, typename T = typename type_get<sizeof...(Args) - I - 1, Args...>::type>
T& get_unsafe()
{
return get_unsafe<T>();
}
size_t get_type_idx() const { return type_id; }
template <typename T>
bool is() const
{
return type_indexer<T, Args...>::index == type_id;
}
template <typename T>
T& get()
{
if (is<T>()) {
return get_unsafe<T>();
}
throw choice_details::bad_choice_access{"in get<T>"};
}
template <typename T>
const T& get() const
{
if (is<T>()) {
return get_unsafe<T>();
}
throw choice_details::bad_choice_access{"in get<T>"};
}
template <std::size_t I, typename T = typename choice_details::type_get<sizeof...(Args) - I - 1, Args...>::type>
T& get()
{
if (is<T>()) {
return get_unsafe<T>();
}
throw choice_details::bad_choice_access{"in get<I>"};
}
template <std::size_t I, typename T = typename choice_details::type_get<sizeof...(Args) - I - 1, Args...>::type>
const T& get() const
{
if (is<T>()) {
return get_unsafe<T>();
}
throw choice_details::bad_choice_access{"in get<I>"};
}
template <typename T>
T* get_if()
{
return (is<T>()) ? &get_unsafe<T>() : nullptr;
}
template <typename T>
const T* get_if() const
{
return (is<T>()) ? &get_unsafe<T>() : nullptr;
return get_type_index<T, Args...>() == type_id;
}
template <typename T>
constexpr static bool can_hold_type()
{
return type_indexer<T, Args...>::index != invalid_idx;
return type_list_contains<T, Args...>();
}
};
@ -312,6 +172,7 @@ class choice_t : private choice_details::tagged_union_t<Args...>
using base_t = choice_details::tagged_union_t<Args...>;
public:
using default_type = typename get_index_type<0, Args...>::type;
//! Useful metafunction
template <typename T>
using enable_if_can_hold =
@ -321,16 +182,14 @@ public:
typename std::enable_if<not base_t::template can_hold_type<typename std::decay<T>::type>()>::type;
using base_t::can_hold_type;
using base_t::get;
using base_t::get_if;
using base_t::get_unchecked;
using base_t::is;
template <
typename... Args2,
typename = typename std::enable_if<std::is_constructible<typename base_t::default_type, Args2...>::value>::type>
template <typename... Args2,
typename = typename std::enable_if<std::is_constructible<default_type, Args2...>::value>::type>
explicit choice_t(Args2&&... args) noexcept
{
base_t::template construct_emplace_unsafe<typename base_t::default_type>(std::forward<Args2>(args)...);
base_t::template construct_emplace_unsafe<default_type>(std::forward<Args2>(args)...);
}
choice_t(const choice_t<Args...>& other) noexcept { base_t::copy_unsafe(other); }
@ -379,64 +238,8 @@ public:
}
bool holds_same_type(const choice_t& other) noexcept { return base_t::type_id == other.type_id; }
template <typename Functor>
void visit(Functor&& f)
{
choice_details::visit<Functor, Args...>(*static_cast<base_t*>(this), f);
}
template <typename Functor>
void visit(Functor&& f) const
{
choice_details::visit<Functor, Args...>(*static_cast<const base_t*>(this), f);
}
private:
};
template <typename T, typename Choice>
bool holds_alternative(const Choice& u)
{
return u.template is<T>();
}
template <typename T, typename... Args>
T* get_if(choice_t<Args...>& c)
{
return c.template get_if<T>();
}
template <typename T, typename... Args>
const T* get_if(const choice_t<Args...>& c)
{
return c.template get_if<T>();
}
template <typename T, typename... Args>
T& get(choice_t<Args...>& c)
{
return c.template get<T>();
}
template <std::size_t I, typename... Args>
auto get(const choice_t<Args...>& c) -> decltype(c.template get<I>())
{
return c.template get<I>();
}
template <typename Functor, typename... Args>
void visit(choice_t<Args...>& u, Functor&& f)
{
u.visit(std::forward<Functor>(f));
}
template <typename Functor, typename... Args>
void visit(const choice_t<Args...>& u, Functor&& f)
{
u.visit(std::forward<Functor>(f));
}
} // namespace srslte
#endif // SRSLTE_CHOICE_TYPE_H

View File

@ -22,52 +22,47 @@
#ifndef SRSLTE_FSM_H
#define SRSLTE_FSM_H
#include "choice_type.h"
#include "srslte/common/logmap.h"
#include <cassert>
#include "type_utils.h"
#include <cstdio>
#include <cstring>
#include <limits>
#include <memory>
#include <tuple>
namespace srslte {
//! Helper to print the name of a type for logging
#if defined(__GNUC__) && !defined(__clang__)
template <typename T>
std::string get_type_name()
{
static const char* funcname = __PRETTY_FUNCTION__;
static const std::string s = []() {
static const char* pos1 = strchr(funcname, '=') + 2;
static const char* pos2 = strchr(pos1, ';');
std::string s2{pos1, pos2};
size_t colon_pos = s2.rfind(':');
std::string s3 = colon_pos == std::string::npos ? s2 : s2.substr(colon_pos + 1, s2.size());
return s3.find('>') == std::string::npos ? s3 : s2;
}();
return s;
}
#else
template <typename T>
std::string get_type_name()
{
return "anonymous";
}
#endif
//! Transition Type
template <typename NextState>
struct to_state {
using next_state = NextState;
};
//! This version leverages argument type deduction. (e.g. get_type_name(var))
template <typename T>
std::string get_type_name(const T& t)
{
return get_type_name<T>();
}
template <typename... NextStates>
struct to_states {
template <typename ChosenState>
to_states(to_state<ChosenState>) : state_idx(get_type_index<ChosenState, NextStates...>())
{
}
template <typename State>
bool is() const
{
return get_type_index<State, NextStates...>() == state_idx;
}
size_t get_type_idx() const { return state_idx; }
size_t state_idx;
};
//! Return for when there is no state transition
struct same_state {
};
//! Forward declaration
template <typename Derived>
class fsm_t;
namespace fsm_details {
//! Visitor to get a state's name string
@ -77,15 +72,15 @@ struct state_name_visitor {
{
name = get_type_name(s);
}
std::string name = "invalid state";
std::string name = "invalid";
};
//! Visitor to convert a type inside a choice to another choice
//! Visitor to convert a to_state<States...> back to a single state
template <typename FSM, typename PrevState>
struct choice_to_state_visitor {
choice_to_state_visitor(FSM* f_, PrevState* p_) : f(f_), p(p_) {}
struct to_state_visitor {
to_state_visitor(FSM* f_, PrevState* p_) : f(f_), p(p_) {}
template <typename State>
void operator()(State& s);
void operator()();
FSM* f;
PrevState* p;
};
@ -99,25 +94,37 @@ struct enter_visitor {
FSM* f;
};
//! Helper metafunctions
template <typename FSM, typename State>
using enable_if_fsm_state = typename std::enable_if<FSM::template can_hold_state<State>()>::type;
template <typename FSM, typename State>
using disable_if_fsm_state = typename std::enable_if<not FSM::template can_hold_state<State>()>::type;
template <typename FSM>
constexpr bool is_fsm()
{
return std::is_base_of<fsm_t<FSM>, FSM>::value;
}
template <typename FSM>
constexpr bool is_nested_fsm()
{
return is_fsm<FSM>() and FSM::is_nested;
}
struct fsm_helper {
//! Metafunction to determine if FSM can hold given State type
template <typename FSM>
using get_fsm_state_list = decltype(std::declval<typename FSM::derived_view>().states);
template <typename FSM, typename State>
using enable_if_fsm_state = typename get_fsm_state_list<FSM>::template enable_if_can_hold<State>;
template <typename FSM, typename State>
using disable_if_fsm_state = typename get_fsm_state_list<FSM>::template disable_if_can_hold<State>;
using fsm_state_list_type = decltype(std::declval<typename FSM::derived_view>().states);
//! Call FSM/State enter method
template <typename FSM, typename State>
static auto call_enter(FSM* f, State* s) -> decltype(s->derived()->parent_fsm(), void())
static typename std::enable_if<is_fsm<State>()>::type call_enter(FSM* f, State* s)
{
f->enter(*s);
fsm_details::enter_visitor<typename State::derived_view> visitor{s->derived()};
s->derived()->states.visit(visitor);
srslte::visit(visitor, s->derived()->states);
}
template <typename FSM, typename State, typename... Args>
static void call_enter(FSM* f, State* s, Args&&...)
static typename std::enable_if<not is_fsm<State>()>::type call_enter(FSM* f, State* s)
{
f->enter(*s);
}
@ -130,30 +137,30 @@ struct fsm_helper {
}
//! TargetState is type-erased (a choice). Apply its stored type to the fsm current state
template <typename FSM, typename... Args, typename PrevState>
static void handle_state_change(FSM* f, choice_t<Args...>* s, PrevState* p)
static void handle_state_change(FSM* f, to_states<Args...>* s, PrevState* p)
{
choice_to_state_visitor<FSM, PrevState> visitor{f, p};
s->visit(visitor);
to_state_visitor<FSM, PrevState> visitor{f, p};
srslte::static_visit(visitor, *s);
}
//! Simple state transition in FSM (no same_state of entry in nested FSM)
template <typename FSM, typename State, typename PrevState>
static enable_if_fsm_state<FSM, State> handle_state_change(FSM* f, State* s, PrevState* p)
static auto handle_state_change(FSM* f, to_state<State>* s, PrevState* p) -> enable_if_fsm_state<FSM, State>
{
static_assert(not std::is_same<State, PrevState>::value, "State cannot transition to itself.\n");
f->exit(srslte::get<PrevState>(f->states));
f->states.transit(std::move(*s));
f->exit(f->states.template get_unchecked<PrevState>());
f->states.template transit<State>();
f->log_h->info("FSM \"%s\": Detected transition \"%s\" -> \"%s\"",
get_type_name<typename FSM::derived_t>().c_str(),
get_type_name<PrevState>().c_str(),
get_type_name<State>().c_str());
call_enter(f, &srslte::get<State>(f->states));
call_enter(f, &f->states.template get_unchecked<State>());
}
//! State not present in current FSM. Attempt state transition in parent FSM in the case of NestedFSM
template <typename FSM, typename State, typename PrevState>
static disable_if_fsm_state<FSM, State> handle_state_change(FSM* f, State* s, PrevState* p)
static auto handle_state_change(FSM* f, to_state<State>* s, PrevState* p) -> disable_if_fsm_state<FSM, State>
{
static_assert(FSM::is_nested, "State is not present in the FSM list of valid states");
f->exit(srslte::get<PrevState>(f->states));
f->exit(f->states.template get_unchecked<PrevState>());
handle_state_change(f->parent_fsm()->derived(), s, static_cast<typename FSM::derived_t*>(f));
}
@ -203,9 +210,10 @@ struct fsm_helper {
template <typename FSM, typename PrevState>
template <typename State>
void choice_to_state_visitor<FSM, PrevState>::operator()(State& s)
void to_state_visitor<FSM, PrevState>::operator()()
{
fsm_helper::handle_state_change(f, &s, p);
to_state<State> t;
fsm_helper::handle_state_change(f, &t, p);
}
template <typename FSM>
@ -219,10 +227,10 @@ void enter_visitor<FSM>::operator()(State&& s)
//! Gets the typename currently stored in the choice_t
template <typename... Args>
std::string get_type_name(const srslte::choice_t<Args...>& t)
std::string get_type_name(const srslte::to_states<Args...>& t)
{
fsm_details::state_name_visitor v{};
t.visit(v);
srslte::visit(v, t);
return v.name;
}
@ -232,6 +240,7 @@ class fsm_t
{
protected:
using base_t = fsm_t<Derived>;
//! get access to derived protected members from the base
class derived_view : public Derived
{
@ -251,24 +260,65 @@ protected:
public:
static const bool is_nested = false;
template <typename NextState>
using to_state = srslte::to_state<NextState>;
template <typename... NextStates>
using to_states = srslte::to_states<NextStates...>;
//! Struct used to store FSM states
template <typename... States>
struct state_list : public choice_t<States...> {
using base_t = choice_t<States...>;
struct state_list : public std::tuple<States...> {
using tuple_base_t = std::tuple<States...>;
template <typename... Args>
state_list(fsm_t<Derived>* f, Args&&... args) : base_t(std::forward<Args>(args)...)
state_list(fsm_t<Derived>* f, Args&&... args) : tuple_base_t(std::forward<Args>(args)...)
{
if (not Derived::is_nested) {
// If Root FSM, call initial state enter method
fsm_details::enter_visitor<derived_view> visitor{f->derived()};
f->derived()->states.visit(visitor);
srslte::visit(visitor, *this);
}
}
template <typename State>
void transit(State&& s)
bool is() const
{
this->template emplace<State>(std::forward<State>(s));
return type_idx<State>() == current_idx;
}
template <typename State>
State& get_unchecked()
{
return std::get<type_idx<State>()>(*this);
}
template <typename State>
const State& get_unchecked() const
{
return std::get<type_idx<State>()>(*this);
}
template <typename NextState>
void transit()
{
current_idx = type_idx<NextState>();
}
template <typename State>
constexpr static bool can_hold_type()
{
return srslte::type_list_contains<State, States...>();
}
template <typename State>
constexpr static size_t type_idx()
{
return get_type_index<State, States...>();
}
size_t get_type_idx() const { return current_idx; }
private:
size_t current_idx = 0;
};
explicit fsm_t(srslte::log_ref log_) : log_h(log_) {}
@ -278,7 +328,7 @@ public:
void trigger(Ev&& e)
{
fsm_details::fsm_helper::trigger_visitor<derived_view, Ev> visitor{derived(), std::forward<Ev>(e)};
derived()->states.visit(visitor);
srslte::visit(visitor, derived()->states);
}
template <typename State>
@ -290,13 +340,13 @@ public:
template <typename State>
const State* get_state() const
{
return srslte::get_if<State>(derived()->states);
return is_in_state<State>() ? &derived()->states.template get_unchecked<State>() : nullptr;
}
std::string get_state_name() const
{
fsm_details::state_name_visitor visitor{};
derived()->states.visit(visitor);
srslte::visit(visitor, derived()->states);
return visitor.name;
}
@ -304,7 +354,7 @@ public:
template <typename State>
constexpr static bool can_hold_state()
{
return fsm_details::fsm_helper::get_fsm_state_list<fsm_t<Derived> >::template can_hold_type<State>();
return fsm_details::fsm_helper::fsm_state_list_type<fsm_t<Derived> >::template can_hold_type<State>();
}
void set_fsm_event_log_level(srslte::LOG_LEVEL_ENUM e) { fsm_event_log_level = e; }
@ -395,7 +445,7 @@ struct proc_launch_ev {
explicit proc_launch_ev(Args&&... args_) : args(std::forward<Args>(args_)...) {}
};
template <typename Derived, typename Result = srslte::same_state>
template <typename Derived, typename Result = std::true_type>
class proc_fsm_t : public fsm_t<Derived>
{
using fsm_type = Derived;
@ -425,9 +475,6 @@ public:
struct idle_st {
};
struct complete_st {
complete_st(bool success_) : success(success_) {}
bool success;
Result result;
};
explicit proc_fsm_t(srslte::log_ref log_) : fsm_t<Derived>(log_) {}
@ -448,10 +495,32 @@ protected:
}
void enter(complete_st& s) { trigger(reset_ev{}); }
auto react(complete_st& s, reset_ev ev) -> idle_st { return {}; }
auto react(complete_st& s, reset_ev ev) -> to_state<idle_st> { return {}; }
srslte::to_state<complete_st> set_success(Result&& r = {})
{
result = std::forward<Result>(r);
success = true;
return {};
}
srslte::to_state<complete_st> set_failure()
{
success = false;
return {};
}
bool is_success() const { return success; }
Result& get_result() const
{
if (is_success()) {
return result;
}
throw bad_type_access{"in proc_fsm_t::get_result"};
}
private:
int launch_counter = 0;
int launch_counter = 0;
bool success = false;
Result result;
};
} // namespace srslte

View File

@ -0,0 +1,263 @@
/*
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLTE_TYPE_UTILS_H
#define SRSLTE_TYPE_UTILS_H
#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <type_traits>
namespace srslte {
//! Helper to print the name of a type for logging
/**
* @brief Helper function that returns a type name string
*/
#if defined(__GNUC__) && !defined(__clang__)
template <typename T>
std::string get_type_name()
{
static const char* funcname = __PRETTY_FUNCTION__;
static const std::string s = []() {
static const char* pos1 = strchr(funcname, '=') + 2;
static const char* pos2 = strchr(pos1, ';');
std::string s2{pos1, pos2};
size_t colon_pos = s2.rfind(':');
std::string s3 = colon_pos == std::string::npos ? s2 : s2.substr(colon_pos + 1, s2.size());
return s3.find('>') == std::string::npos ? s3 : s2;
}();
return s;
}
#else
template <typename T>
std::string get_type_name()
{
return "anonymous";
}
#endif
//! This version leverages argument type deduction for shorter syntax. (e.g. get_type_name(var))
template <typename T>
std::string get_type_name(const T& t)
{
return get_type_name<T>();
}
constexpr size_t invalid_type_index = std::numeric_limits<size_t>::max();
namespace type_utils_details {
//! Get Index of a type in a list of types (in reversed order)
template <typename T, typename... Types>
struct type_list_reverse_index;
template <typename T, typename First, typename... Types>
struct type_list_reverse_index<T, First, Types...> {
static constexpr size_t index =
std::is_same<T, First>::value ? sizeof...(Types) : type_list_reverse_index<T, Types...>::index;
};
template <typename T>
struct type_list_reverse_index<T> {
static constexpr size_t index = invalid_type_index;
};
//! Get a type of an index in a list of types
template <std::size_t I, typename... Types>
struct get_type_reverse;
template <std::size_t I, typename First, typename... Types>
struct get_type_reverse<I, First, Types...> {
using type =
typename std::conditional<I == sizeof...(Types), First, typename get_type_reverse<I, Types...>::type>::type;
};
template <std::size_t I>
struct get_type_reverse<I> {
using type = void;
};
template <typename F, typename... Types>
struct static_visit_impl;
template <typename F, typename First, typename... Types>
struct static_visit_impl<F, First, Types...> {
static void apply(size_t idx, F&& f)
{
if (idx == sizeof...(Types)) {
f.template operator()<First>();
} else {
static_visit_impl<F, Types...>::apply(idx, std::forward<F>(f));
}
}
};
template <typename F, typename First>
struct static_visit_impl<F, First> {
static void apply(size_t idx, F&& f) { f.template operator()<First>(); }
};
/**
* @brief visit pattern implementation
* @tparam F functor
* @tparam V tagged union type
* @tparam Types remaining types to iterate
*/
template <typename F, typename TypeList, typename... Types>
struct visit_impl;
template <typename F, typename TypeList, typename First, typename... Types>
struct visit_impl<F, TypeList, First, Types...> {
static void apply(TypeList& c, F&& f)
{
if (c.template is<First>()) {
f(c.template get_unchecked<First>());
} else {
visit_impl<F, TypeList, Types...>::apply(c, std::forward<F>(f));
}
}
static void apply(const TypeList& c, F&& f)
{
if (c.template is<First>()) {
f(c.template get_unchecked<First>());
} else {
visit_impl<F, TypeList, Types...>::apply(c, std::forward<F>(f));
}
}
};
template <typename F, typename TypeList, typename First>
struct visit_impl<F, TypeList, First> {
static void apply(TypeList& c, F&& f) { f(c.template get_unchecked<First>()); }
static void apply(const TypeList& c, F&& f) { f(c.template get_unchecked<First>()); }
};
} // namespace type_utils_details
class bad_type_access : public std::runtime_error
{
public:
explicit bad_type_access(const std::string& what_arg) : runtime_error(what_arg) {}
explicit bad_type_access(const char* what_arg) : runtime_error(what_arg) {}
};
//! Get index of T in Types...
template <typename T, typename... Types>
constexpr size_t get_type_index()
{
using namespace type_utils_details;
return (type_list_reverse_index<T, Types...>::index == invalid_type_index)
? invalid_type_index
: sizeof...(Types) - type_list_reverse_index<T, Types...>::index - 1;
}
template <typename T, typename... Types>
constexpr bool type_list_contains()
{
return get_type_index<T, Types...>() != invalid_type_index;
}
template <size_t I, typename... Types>
struct get_index_type {
using type = typename type_utils_details::get_type_reverse<sizeof...(Types) - I - 1, Types...>::type;
};
//! srslte::get<Type> access methods
template <typename T, typename TypeContainer>
T* get_if(TypeContainer& c)
{
return (c.template is<T>()) ? &c.template get_unchecked<T>() : nullptr;
}
template <typename T, typename TypeContainer>
const T* get_if(const TypeContainer& c)
{
return (c.template is<T>()) ? &c.template get_unchecked<T>() : nullptr;
}
template <typename T, typename TypeContainer>
T& get(TypeContainer& c)
{
if (c.template is<T>()) {
return c.template get_unchecked<T>();
}
throw bad_type_access{"in get<T>"};
}
template <typename T, typename TypeContainer>
const T& get(const TypeContainer& c)
{
if (c.template is<T>()) {
return c.template get_unchecked<T>();
}
throw bad_type_access{"in get<T>"};
}
template <size_t I,
template <typename...> class TypeContainer,
typename... Args,
typename T = typename get_index_type<I, Args...>::type>
T& get(TypeContainer<Args...>& c)
{
return get<T>(c);
}
template <size_t I,
template <typename...> class TypeContainer,
typename... Args,
typename T = typename get_index_type<I, Args...>::type>
const T& get(const TypeContainer<Args...>& c)
{
return get<T>(c);
}
//! Function to static visit a template type that holds many variadic args
template <typename Visitor, typename... Args>
void static_visit(Visitor&& f, size_t current_idx)
{
type_utils_details::static_visit_impl<Visitor, Args...>::apply(sizeof...(Args) - current_idx - 1,
std::forward<Visitor>(f));
}
template <typename Visitor, template <typename...> class TypeSet, typename... Args>
void static_visit(Visitor&& f, const TypeSet<Args...>& tset)
{
static_visit<Visitor, Args...>(std::forward<Visitor>(f), tset.get_type_idx());
}
//! Function to visit a template type that holds many variadic args
template <typename Visitor, template <typename...> class TypeSet, typename... Args>
void visit(Visitor&& f, TypeSet<Args...>& tset)
{
using namespace type_utils_details;
visit_impl<Visitor, TypeSet<Args...>, Args...>::apply(tset, std::forward<Visitor>(f));
}
template <typename Visitor, template <typename...> class TypeSet, typename... Args>
void visit(Visitor&& f, const TypeSet<Args...>& tset)
{
using namespace type_utils_details;
visit_impl<Visitor, TypeSet<Args...>, Args...>::apply(tset, std::forward<Visitor>(f));
}
} // namespace srslte
#endif // SRSLTE_TYPE_UTILS_H

View File

@ -63,17 +63,16 @@ namespace choice_details {
static_assert(static_max<1, 2>::value == 2, "StaticMax not working");
static_assert(static_max<2, 1>::value == 2, "StaticMax not working");
static_assert(type_indexer<double, char, float, int, long, double>::index == 0, "Type indexer not working");
static_assert(type_indexer<double, double, float, int, long, char>::index == 4, "Type indexer not working");
static_assert(type_indexer<double, char, float, int>::index == invalid_idx, "Type Indexer not working");
static_assert(get_type_index<double, char, float, int, long, double>() == 4, "Type indexer not working");
static_assert(get_type_index<double, double, float, int, long, char>() == 0, "Type indexer not working");
static_assert(get_type_index<double, char, float, int>() == invalid_type_index, "Type Indexer not working");
static_assert(sizeof(choice_storage_t<5, 4>) == 8, "Size of storage wrongly computed");
static_assert(alignof(choice_storage_t<5, 4>) == 4, "Alignment of storage wrongly computed");
static_assert(std::is_same<typename type_get<0, char, float, int, long, double>::type, double>::value,
static_assert(std::is_same<typename get_index_type<0, char, float, int, long, double>::type, char>::value,
"type index-based search not working");
static_assert(std::is_same<typename type_get<3, char, float, int, long, double>::type, float>::value,
static_assert(std::is_same<typename get_index_type<3, char, float, int, long, double>::type, long>::value,
"type index-based search not working");
static_assert(std::is_same<tagged_union_t<char, int, double>::default_type, char>::value,
"Default type is incorrect\n");
static_assert(std::is_same<choice_t<char, int, double>::default_type, char>::value, "Default type is incorrect\n");
static_assert(tagged_union_t<char, int, double>::can_hold_type<int>(), "Can hold type implementation is incorrect\n");
static_assert(not tagged_union_t<char, int, double>::can_hold_type<uint8_t>(),
"Can hold type implementation is incorrect\n");
@ -91,8 +90,7 @@ int test_tagged_union()
tagged_union_t<char, int, double, C> u;
u.construct_unsafe(5);
TESTASSERT(u.is<int>());
TESTASSERT(u.get_unsafe<int>() == 5);
TESTASSERT(u.get_unsafe<1>() == 5);
TESTASSERT(u.get_unchecked<int>() == 5);
u.destroy_unsafe<int>();
TESTASSERT(C::counter == 0);
@ -106,8 +104,8 @@ int test_tagged_union()
int test_choice()
{
using srslte::bad_type_access;
using srslte::choice_t;
using srslte::choice_details::bad_choice_access;
TESTASSERT(C::counter == 0);
TESTASSERT(D::counter == 0);
@ -118,47 +116,47 @@ int test_choice()
// TEST: correct construction, holding the right type and value
choice_t<char, int, double, C> c, c2{i}, c3{c0};
TESTASSERT(c.is<char>());
TESTASSERT(c2.is<int>() and c2.get<int>() == i and *c2.get_if<int>() == i);
TESTASSERT(c2.get<1>() == c2.get<int>());
TESTASSERT(c2.is<int>() and srslte::get<int>(c2) == i and *srslte::get_if<int>(c2) == i);
TESTASSERT(srslte::get<1>(c2) == srslte::get<int>(c2));
TESTASSERT(c3.is<C>());
TESTASSERT(C::counter == 2);
// TEST: Invalid member access. get<>() should throw
TESTASSERT(c2.get_if<char>() == nullptr);
TESTASSERT(srslte::get_if<char>(c2) == nullptr);
bool catched = false;
try {
char n = '1';
n = c2.get<char>();
n = srslte::get<char>(c2);
TESTASSERT(n == '1');
} catch (bad_choice_access& e) {
} catch (bad_type_access& e) {
catched = true;
}
TESTASSERT(catched);
// TEST: simple emplace after construction
c2 = 'c';
TESTASSERT(c2.is<char>() and c2.get<char>() == 'c');
TESTASSERT(c2.is<char>() and srslte::get<char>(c2) == 'c');
// TEST: copy ctor test.
choice_t<char, int, double, C> c5{c3};
TESTASSERT(C::counter == 3);
TESTASSERT(c5.is<C>());
TESTASSERT(c5.get_if<C>() == &c5.get<C>());
TESTASSERT(srslte::get_if<C>(c5) == &srslte::get<C>(c5));
// TEST: copy assignment
c = c5;
TESTASSERT(C::counter == 4);
TESTASSERT(c.is<C>() and c.get_if<C>() != c5.get_if<C>());
TESTASSERT(c.is<C>() and srslte::get_if<C>(c) != srslte::get_if<C>(c5));
c = c2;
TESTASSERT(C::counter == 3);
TESTASSERT(c2.is<char>() and c.get<char>() == 'c');
TESTASSERT(c2.is<char>() and srslte::get<char>(c) == 'c');
}
TESTASSERT(C::counter == 0);
TESTASSERT(D::counter == 0);
{
choice_t<char, int, double, C, D> c, c2{5.0}, c3{C{}}, c4{D{}};
TESTASSERT(c.is<char>());
TESTASSERT(c2.is<double>() and c2.get<double>() == 5.0 and *c2.get_if<double>() == 5.0);
TESTASSERT(c2.is<double>() and srslte::get<double>(c2) == 5.0 and *srslte::get_if<double>(c2) == 5.0);
TESTASSERT(c3.is<C>());
TESTASSERT(c4.is<D>());
TESTASSERT(C::counter == 1);
@ -182,7 +180,7 @@ int test_choice()
TESTASSERT(D::counter == 3);
c = std::move(c2);
TESTASSERT(c.is<double>() and c2.is<double>() and c.holds_same_type(c2));
TESTASSERT(c.get<double>() == c2.get<double>());
TESTASSERT(srslte::get<double>(c) == srslte::get<double>(c2));
TESTASSERT(C::counter == 2 and D::counter == 2);
}
TESTASSERT(C::counter == 0);
@ -213,26 +211,26 @@ int test_visit()
// TEST: visitor hits integer type which is noop
EVisitor v;
srslte::visit(c, v);
TESTASSERT(c.is<int>() and c.get<int>() == 5);
srslte::visit(v, c);
TESTASSERT(c.is<int>() and srslte::get<int>(c) == 5);
TESTASSERT(v.pdu == nullptr);
// TEST: visitor hits type E and steals pdu
E e;
e.pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
c = std::move(e);
TESTASSERT(c.is<E>() and c.get<E>().pdu != nullptr);
srslte::visit(c, v);
TESTASSERT(c.is<E>() and srslte::get<E>(c).pdu != nullptr);
srslte::visit(v, c);
TESTASSERT(v.pdu != nullptr);
TESTASSERT(c.is<E>() and c.get<E>().pdu == nullptr);
TESTASSERT(c.is<E>() and srslte::get<E>(c).pdu == nullptr);
// TEST: visitor hits type E and steals pdu. Second type called there is no pdu to steal.
v.pdu = nullptr;
e.pdu = srslte::allocate_unique_buffer(*srslte::byte_buffer_pool::get_instance());
c = std::move(e);
c.visit(v);
srslte::visit(v, c);
TESTASSERT(v.pdu != nullptr);
c.visit(v);
srslte::visit(v, c);
TESTASSERT(v.pdu == nullptr);
return SRSLTE_SUCCESS;

View File

@ -42,7 +42,7 @@ public:
struct state1 {
};
fsm1(srslte::log_ref log_) : srslte::fsm_t<fsm1>(log_) {}
explicit fsm1(srslte::log_ref log_) : srslte::fsm_t<fsm1>(log_) {}
// this state is another FSM
class fsm2 : public srslte::nested_fsm_t<fsm2, fsm1>
@ -52,7 +52,7 @@ public:
struct state_inner {
};
fsm2(fsm1* f_) : nested_fsm_t(f_) {}
explicit fsm2(fsm1* f_) : nested_fsm_t(f_) {}
~fsm2() { log_h->info("%s being destroyed!", get_type_name(*this).c_str()); }
protected:
@ -65,7 +65,7 @@ public:
// FSM2 transitions
auto react(state_inner& s, ev1 e) -> srslte::same_state;
auto react(state_inner& s, ev2 e) -> state1;
auto react(state_inner& s, ev2 e) -> srslte::to_state<state1>;
// list of states
state_list<state_inner> states{this};
@ -87,14 +87,14 @@ protected:
void enter(state1& s);
// transitions
auto react(idle_st& s, ev1 e) -> state1;
auto react(state1& s, ev1 e) -> fsm2;
auto react(state1& s, ev2 e) -> srslte::choice_t<idle_st, fsm2>;
auto react(idle_st& s, ev1 e) -> srslte::to_state<state1>;
auto react(state1& s, ev1 e) -> srslte::to_state<fsm2>;
auto react(state1& s, ev2 e) -> srslte::to_states<idle_st, fsm2>;
void foo(ev1 e) { foo_counter++; }
// list of states
state_list<idle_st, state1, fsm2> states{this, idle_st{}};
state_list<idle_st, state1, fsm2> states = {this, idle_st{}, state1{}, fsm2{this}};
};
void fsm1::enter(idle_st& s)
@ -109,33 +109,33 @@ void fsm1::enter(state1& s)
}
// FSM event handlers
auto fsm1::fsm2::react(state_inner& s, ev1 e) -> srslte::same_state
auto fsm1::fsm2::react(state_inner& s, ev1) -> srslte::same_state
{
log_h->info("fsm2::state_inner::react called\n");
return {};
}
auto fsm1::fsm2::react(state_inner& s, ev2 e) -> state1
auto fsm1::fsm2::react(state_inner& s, ev2) -> srslte::to_state<state1>
{
log_h->info("fsm2::state_inner::react called\n");
return {};
}
auto fsm1::react(idle_st& s, ev1 e) -> state1
auto fsm1::react(idle_st& s, ev1 e) -> srslte::to_state<state1>
{
log_h->info("%s::react called\n", srslte::get_type_name(s).c_str());
foo(e);
return {};
}
auto fsm1::react(state1& s, ev1 e) -> fsm2
auto fsm1::react(state1& s, ev1) -> srslte::to_state<fsm2>
{
log_h->info("%s::react called\n", srslte::get_type_name(s).c_str());
return {this};
return {};
}
auto fsm1::react(state1& s, ev2 e) -> srslte::choice_t<idle_st, fsm2>
auto fsm1::react(state1& s, ev2) -> srslte::to_states<idle_st, fsm2>
{
log_h->info("%s::react called\n", srslte::get_type_name(s).c_str());
return idle_st{};
return srslte::to_state<idle_st>{};
}
// Static Checks
@ -143,14 +143,15 @@ auto fsm1::react(state1& s, ev2 e) -> srslte::choice_t<idle_st, fsm2>
namespace srslte {
namespace fsm_details {
static_assert(std::is_same<fsm_helper::get_fsm_state_list<fsm1>,
static_assert(is_fsm<fsm1>(), "invalid metafunction\n");
static_assert(is_nested_fsm<fsm1::fsm2>(), "invalid metafunction\n");
static_assert(std::is_same<fsm_helper::fsm_state_list_type<fsm1>,
fsm1::state_list<fsm1::idle_st, fsm1::state1, fsm1::fsm2> >::value,
"get state list failed\n");
static_assert(std::is_same<fsm_helper::enable_if_fsm_state<fsm1, fsm1::idle_st>, void>::value,
static_assert(fsm1::can_hold_state<fsm1::state1>(), "failed can_hold_state check\n");
static_assert(std::is_same<enable_if_fsm_state<fsm1, fsm1::idle_st>, void>::value, "get state list failed\n");
static_assert(std::is_same<disable_if_fsm_state<fsm1, fsm1::fsm2::state_inner>, void>::value,
"get state list failed\n");
static_assert(std::is_same<fsm_helper::disable_if_fsm_state<fsm1, fsm1::fsm2::state_inner>, void>::value,
"get state list failed\n");
static_assert(fsm1::can_hold_state<fsm1::state1>(), "can hold state method failed\n");
} // namespace fsm_details
} // namespace srslte
@ -190,7 +191,7 @@ int test_hsm()
// Moving fsm2 -> state1
f.trigger(ev2{});
TESTASSERT(std::string{f.get_state_name()} == "state1");
TESTASSERT(f.get_state_name() == "state1");
TESTASSERT(f.is_in_state<fsm1::state1>());
TESTASSERT(f.state1_enter_counter == 2);
@ -208,7 +209,7 @@ int test_hsm()
return SRSLTE_SUCCESS;
}
///////////////////////////
/////////////////////////////
struct procevent1 {
};
@ -224,35 +225,35 @@ public:
protected:
// Transitions
auto react(idle_st& s, srslte::proc_launch_ev<int*> ev) -> procstate1;
auto react(procstate1& s, procevent1 ev) -> complete_st;
auto react(procstate1& s, procevent2 ev) -> complete_st;
auto react(complete_st& s, reset_ev ev) -> idle_st;
auto react(idle_st& s, srslte::proc_launch_ev<int*> ev) -> to_state<procstate1>;
auto react(procstate1& s, procevent1 ev) -> to_state<complete_st>;
auto react(procstate1& s, procevent2 ev) -> to_state<complete_st>;
auto react(complete_st& s, reset_ev ev) -> to_state<idle_st>;
// example of uncaught event handling
void unhandled_event(int e) { log_h->info("I dont know how to handle an \"int\" event\n"); }
state_list<idle_st, procstate1, complete_st> states{this};
state_list<idle_st, procstate1, complete_st> states{this, idle_st{}, procstate1{}, complete_st{}};
};
auto proc1::react(idle_st& s, srslte::proc_launch_ev<int*> ev) -> procstate1
auto proc1::react(idle_st& s, srslte::proc_launch_ev<int*> ev) -> to_state<procstate1>
{
log_h->info("started!\n");
return {};
}
auto proc1::react(procstate1& s, procevent1 ev) -> complete_st
auto proc1::react(procstate1& s, procevent1 ev) -> to_state<complete_st>
{
log_h->info("success!\n");
return {true};
return set_success();
}
auto proc1::react(procstate1& s, procevent2 ev) -> complete_st
auto proc1::react(procstate1& s, procevent2 ev) -> to_state<complete_st>
{
log_h->info("failure!\n");
return {false};
return set_failure();
}
auto proc1::react(complete_st& s, reset_ev ev) -> idle_st
auto proc1::react(complete_st& s, reset_ev ev) -> to_state<idle_st>
{
log_h->info("propagate results %s\n", s.success ? "success" : "failure");
log_h->info("propagate results %s\n", is_success() ? "success" : "failure");
return {};
}