Skip to content

Instantly share code, notes, and snippets.

@bryceschober
Created December 15, 2023 19:02
Show Gist options
  • Save bryceschober/7cb64eec96c6043f3e98f1e7a9eb174c to your computer and use it in GitHub Desktop.
Save bryceschober/7cb64eec96c6043f3e98f1e7a9eb174c to your computer and use it in GitHub Desktop.
compile-init-build single-header build cib.hpp
/*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the "Software") to use, reproduce, display, distribute,
* execute, and transmit the Software, and to prepare derivative works of the
* Software, and to permit third-parties to whom the Software is furnished to
* do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including
* the above license grant, this restriction and the following disclaimer,
* must be included in all copies of the Software, in whole or in part, and
* all derivative works of the Software, unless such copies or derivative
* works are solely in the form of machine-executable object code generated by
* a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
/*
* cib - Compile-time Initialization and Build
* v1.7.0-335-g822b06d
*
* For the documentation and the latest version, see the official github repo:
* https://github.com/intel/compile-time-init-build
*/
#include <algorithm>
#include <array>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <conc/concurrency.hpp>
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <fmt/compile.h>
#include <fmt/format.h>
#include <iterator>
#include <limits>
#include <optional>
#include <span>
#include <stdx/compiler.hpp>
#include <stdx/concepts.hpp>
#include <stdx/ct_conversions.hpp>
#include <stdx/ct_string.hpp>
#include <stdx/cx_multimap.hpp>
#include <stdx/cx_set.hpp>
#include <stdx/cx_vector.hpp>
#include <stdx/panic.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>
#include <stdx/type_traits.hpp>
#include <stdx/utility.hpp>
#include <string_view>
#include <type_traits>
#include <utility>
namespace cib {
/**
* Describe a builder to cib.
*
* @tparam Builder
* The initial builder type cib should use when creating a builder.
* This is only the initial type, the Builder::add(...) function
* may return a different type and cib will track that correctly.
*
* @tparam Interface
* The type-erased interface services built with this builder
* will implement. For example, cib::callback allows many other
* callables to get executed when its service gets invoked. The
* type-erased interface for cib::callback is a function pointer.
*
* @see cib::built
*
* @example cib::callback_meta
*/
template <typename Builder, typename Interface> struct builder_meta {
using builder_t = Builder;
using interface_t = Interface;
};
template <typename BuilderMeta>
using builder_t = typename BuilderMeta::builder_t;
template <typename BuilderMeta>
using interface_t = typename BuilderMeta::interface_t;
} // namespace cib
namespace cib {
/**
* Pointer to a built service implementation.
*
* @tparam ServiceMeta
* Tag name of the service.
*
* @see cib::builder_meta
*/
template <typename ServiceMeta> interface_t<ServiceMeta> service;
} // namespace cib
namespace cib {
/**
* Builder for simple callbacks.
*
* Components can add their own callback function to this builder to be
* executed when the service is executed with the same function arguments.
*
* @tparam NumFuncs
* The number of functions currently registered with this builder.
*
* @tparam ArgTypes
* List of argument types that must be passed into the callback when it is
* invoked.
*
* @see cib::callback_meta
*/
template <int NumFuncs = 0, typename... ArgTypes> struct callback {
using func_ptr_t = void (*)(ArgTypes...);
std::array<func_ptr_t, NumFuncs> funcs{};
/**
* Add a function to be executed when the callback service is invoked.
*
* Do not call this function directly. The library will add functions
* to service builders based on a project's cib::config and cib::extend
* declarations.
*
* @return
* A version of this callback builder with the addition of func.
*
* @see cib::extend
* @see cib::nexus
*/
template <std::convertible_to<func_ptr_t>... Fs>
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
[[nodiscard]] constexpr auto add(Fs &&...fs) const {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return callback<NumFuncs + sizeof...(Fs), ArgTypes...>{
{funcs[Is]..., std::forward<Fs>(fs)...}};
}(std::make_index_sequence<NumFuncs>{});
}
/**
* Build and return a function pointer to the implemented callback
* builder. Used by cib nexus to automatically build an initialized
* builder.
*
* Do not call directly.
*
* @tparam BuilderValue
* Struct that contains a "static constexpr auto value" field with the
* initialized builder.
*
* @return
* Function pointer to the implemented callback service.
*/
template <typename BuilderValue>
[[nodiscard]] CONSTEVAL static auto build() {
return run<BuilderValue>;
}
private:
/**
* Runtime implementation of a callback service.
*
* Calls each registered function in an undefined order. The order
* functions are called should not be depended upon and could
* change from one release to the next.
*
* This function will be available from nexus::builder<...> or
* cib::built<...>.
*
* @tparam BuilderValue
* A type that contains a constexpr static value field with the
* fully initialized callback builder.
*
* @param args
* The arguments to be passed to every registered function.
*
* @see cib::nexus
* @see cib::built
*/
template <typename BuilderValue> static void run(ArgTypes... args) {
constexpr auto handler_builder = BuilderValue::value;
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
(handler_builder.funcs[Is](args...), ...);
}(std::make_index_sequence<NumFuncs>{});
}
};
/**
* Extend this to create named callback services.
*
* Types that extend callback_meta can be used as unique names with
* cib::exports and cib::extend.
*
* @tparam ArgTypes
* The function arguments that must be passed into the callback
* services implementation. any_t function registered with this
* callback service must also have a compatible signature.
*
* @see cib::exports
* @see cib::extend
*/
template <typename... ArgTypes>
struct callback_meta : public cib::builder_meta<callback<0, ArgTypes...>,
void (*)(ArgTypes...)> {};
} // namespace cib
namespace cib::detail {
struct config_item {
template <typename... Args>
[[nodiscard]] constexpr auto extends_tuple(Args const &...) const {
return stdx::make_tuple();
}
template <typename... InitArgs>
[[nodiscard]] constexpr auto exports_tuple(InitArgs const &...) const {
return stdx::make_tuple();
}
};
} // namespace cib::detail
namespace cib::detail {
template <typename... Components>
struct components : public detail::config_item {
template <typename... Args>
[[nodiscard]] constexpr auto extends_tuple(Args const &...args) const {
return stdx::tuple_cat(Components::config.extends_tuple(args...)...);
}
template <typename... Args>
[[nodiscard]] constexpr auto exports_tuple(Args const &...args) const {
return stdx::tuple_cat(Components::config.exports_tuple(args...)...);
}
};
} // namespace cib::detail
namespace cib::detail {
template <auto Value>
constexpr static auto as_constant_v =
std::integral_constant<std::remove_cvref_t<decltype(Value)>, Value>{};
template <auto... Args> struct args {
constexpr static auto value = stdx::make_tuple(as_constant_v<Args>...);
};
template <typename ConfigArgs, typename... ConfigTs>
struct config : public detail::config_item {
stdx::tuple<ConfigTs...> configs_tuple;
CONSTEVAL explicit config(ConfigArgs, ConfigTs const &...configs)
: configs_tuple{configs...} {}
template <typename... Args>
[[nodiscard]] constexpr auto extends_tuple(Args const &...args) const {
return ConfigArgs::value.apply([&](auto const &...config_args) {
return configs_tuple.apply([&](auto const &...configs_pack) {
return stdx::tuple_cat(
configs_pack.extends_tuple(args..., config_args...)...);
});
});
}
template <typename... Args>
[[nodiscard]] constexpr auto exports_tuple(Args const &...args) const {
return ConfigArgs::value.apply([&](auto const &...config_args) {
return configs_tuple.apply([&](auto const &...configs_pack) {
return stdx::tuple_cat(
configs_pack.exports_tuple(args..., config_args...)...);
});
});
}
};
} // namespace cib::detail
namespace cib::detail {
template <typename Pred, typename... Configs>
requires std::is_default_constructible_v<Pred>
struct conditional : config_item {
detail::config<detail::args<>, Configs...> body;
CONSTEVAL explicit conditional(Configs const &...configs)
: body{{}, configs...} {}
template <typename... Args>
[[nodiscard]] constexpr auto extends_tuple(Args const &...) const {
if constexpr (Pred{}(Args{}...)) {
return body.extends_tuple(Args{}...);
} else {
return stdx::tuple<>{};
}
}
template <typename... Args>
[[nodiscard]] constexpr auto exports_tuple(Args const &...) const {
if constexpr (Pred{}(Args{}...)) {
return body.exports_tuple(Args{}...);
} else {
return stdx::tuple<>{};
}
}
};
} // namespace cib::detail
namespace cib::detail {
template <typename ServiceType, typename... Args>
struct extend : public config_item {
using service_type = ServiceType;
constexpr static auto builder = cib::builder_t<service_type>{};
stdx::tuple<Args...> args_tuple;
CONSTEVAL explicit extend(Args const &...args) : args_tuple{args...} {}
template <typename... InitArgs>
[[nodiscard]] constexpr auto extends_tuple(InitArgs const &...) const {
return stdx::make_tuple(*this);
}
};
} // namespace cib::detail
namespace cib::detail {
template <typename ServiceT, typename BuilderT> struct service_entry {
using Service = ServiceT;
BuilderT builder;
};
template <typename... Services> struct exports : public detail::config_item {
template <typename... InitArgs>
[[nodiscard]] constexpr auto extends_tuple(InitArgs const &...) const {
return stdx::make_tuple(extend<Services>{}...);
}
template <typename... InitArgs>
[[nodiscard]] constexpr auto exports_tuple(InitArgs const &...) const {
return stdx::make_tuple(Services{}...);
}
};
} // namespace cib::detail
namespace cib {
/**
* List of arguments to configure compile-time initialization of components.
*
* @see cib::conditional
*/
template <auto... Args> constexpr static detail::args<Args...> args{};
/**
* Container for project and component configuration declarations.
*
* Each component or project type must contain a static constexpr "config"
* field that contains the cib configuration for that component or project.
* cib::config can be used to compose multiple configuration declarations.
*
* @see cib::components
* @see cib::extend
* @see cib::exports
* @see cib::conditional
*/
template <typename... Configs>
[[nodiscard]] CONSTEVAL auto config(Configs const &...configs) {
return detail::config{args<>, configs...};
}
template <auto... Args, typename... Configs>
[[nodiscard]] CONSTEVAL auto config(detail::args<Args...> config_args,
Configs const &...configs) {
return detail::config{config_args, configs...};
}
/**
* Compose one or more components into a project or larger component.
*
* @tparam Args
* Template instantiation of cib::args filled with compile-time
* configurations values.
*
* @tparam Components
* List of components to be added to the configuration.
*
* @see cib::args
*/
template <typename Args, typename... Components>
constexpr static detail::components<Args, Components...> components{};
/**
* Declare a list of services for use in the project.
*
* @tparam Services
*/
template <typename... Services>
constexpr static detail::exports<Services...> exports{};
/**
* Extend a service with new functionality.
*
* @tparam Service
* Type name of the service to extend.
*
* @tparam ServiceTemplateArgs
* Template arguments to be passed to the service's
* builder add function.
*
* @param args
* Value arguments to be passed to the service's builder add function.
*/
template <typename Service, typename... Args>
[[nodiscard]] CONSTEVAL auto extend(Args const &...args) {
return detail::extend<Service, Args...>{args...};
}
/**
* Include configs based on predicate.
*
* If predicate evaluates to true, then the configs will be added to the
* configuration. Otherwise the configs contained in this conditional
* will not be added.
*/
template <typename Predicate, typename... Configs>
requires std::is_default_constructible_v<Predicate>
[[nodiscard]] CONSTEVAL auto conditional(Predicate const &,
Configs const &...configs) {
return detail::conditional<Predicate, Configs...>{configs...};
}
} // namespace cib
namespace cib {
template <typename T> using extract_service_tag = typename T::Service;
template <typename T>
using get_service = typename std::remove_cvref_t<T>::service_type;
template <typename T>
using get_service_from_tuple = typename std::remove_cvref_t<
decltype(std::declval<T>()[stdx::index<0>])>::service_type;
template <typename Config>
constexpr static auto initialized_builders = transform<extract_service_tag>(
[](auto extensions) {
using exports_tuple = decltype(Config::config.exports_tuple());
using service = get_service_from_tuple<decltype(extensions)>;
static_assert(stdx::contains_type<exports_tuple, service>);
constexpr auto initial_builder = extensions[stdx::index<0>].builder;
auto built_service = extensions.fold_right(
initial_builder, [](auto extension, auto outer_builder) {
return extension.args_tuple.apply(
[&](auto... args) { return outer_builder.add(args...); });
});
return detail::service_entry<service, decltype(built_service)>{
built_service};
},
chunk_by<get_service>(sort<get_service>(Config::config.extends_tuple())));
template <typename Config, typename Tag> struct initialized {
constexpr static auto value =
initialized_builders<Config>.get(stdx::tag<Tag>).builder;
};
} // namespace cib
namespace cib {
/**
* Combines all components in a single location so their features can
* extend services.
*
* @tparam Config
* Project configuration class that contains a single constexpr static
* "config" field describing the cib::config
*
* @see cib::config
*/
template <typename Config> struct nexus {
private:
using this_t = nexus<Config>;
// Workaround unfortunate bug in clang where it can't deduce "auto" sometimes
#define CIB_BUILD_SERVICE \
initialized<Config, Tag>::value.template build<initialized<Config, Tag>>()
public:
template <typename Tag>
constexpr static decltype(CIB_BUILD_SERVICE) service = CIB_BUILD_SERVICE;
#undef CIB_BUILD_SERVICE
static void init() {
auto const service = []<typename T> {
using from_t = std::remove_cvref_t<decltype(this_t::service<T>)>;
using to_t = std::remove_cvref_t<decltype(cib::service<T>)>;
auto &service_impl = this_t::service<T>;
if constexpr (std::is_convertible_v<from_t, to_t>) {
cib::service<T> = service_impl;
} else {
if constexpr (std::is_pointer_v<from_t>) {
cib::service<T> = service_impl;
} else {
cib::service<T> = &service_impl;
}
}
};
initialized_builders<Config>.apply([&]<typename... Ts>(Ts const &...) {
(service.template
operator()<std::remove_cvref_t<typename Ts::Service>>(),
...);
});
}
};
} // namespace cib
namespace flow {
using FunctionPtr = auto (*)() -> void;
} // namespace flow
namespace flow::dsl {
template <typename T>
concept node = requires { typename stdx::remove_cvref_t<T>::is_node; };
template <typename Source, typename Dest> struct edge {
using source_t = Source;
using dest_t = Dest;
};
constexpr inline class get_initials_t {
template <node N> friend constexpr auto tag_invoke(get_initials_t, N &&n) {
return stdx::make_tuple(std::forward<N>(n));
}
public:
template <typename... Ts>
constexpr auto operator()(Ts &&...ts) const
noexcept(noexcept(tag_invoke(std::declval<get_initials_t>(),
std::forward<Ts>(ts)...)))
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
return tag_invoke(*this, std::forward<Ts>(ts)...);
}
} get_initials{};
constexpr inline class get_finals_t {
template <node N> friend constexpr auto tag_invoke(get_finals_t, N &&n) {
return stdx::make_tuple(std::forward<N>(n));
}
public:
template <typename... Ts>
constexpr auto operator()(Ts &&...ts) const
noexcept(noexcept(tag_invoke(std::declval<get_finals_t>(),
std::forward<Ts>(ts)...)))
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
return tag_invoke(*this, std::forward<Ts>(ts)...);
}
} get_finals{};
constexpr inline class get_nodes_t {
template <node N> friend constexpr auto tag_invoke(get_nodes_t, N &&n) {
return stdx::make_tuple(std::forward<N>(n));
}
public:
template <typename... Ts>
constexpr auto operator()(Ts &&...ts) const
noexcept(noexcept(tag_invoke(std::declval<get_nodes_t>(),
std::forward<Ts>(ts)...)))
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
return tag_invoke(*this, std::forward<Ts>(ts)...);
}
} get_nodes{};
constexpr inline class get_edges_t {
friend constexpr auto tag_invoke(get_edges_t, node auto const &) {
return stdx::tuple{};
}
public:
template <typename... Ts>
constexpr auto operator()(Ts &&...ts) const
noexcept(noexcept(tag_invoke(std::declval<get_edges_t>(),
std::forward<Ts>(ts)...)))
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
return tag_invoke(*this, std::forward<Ts>(ts)...);
}
} get_edges{};
} // namespace flow::dsl
namespace logging {
// enum assignment is according to Mipi_Sys-T Severity definition
enum level {
MAX = 0,
FATAL = 1,
ERROR = 2,
WARN = 3,
INFO = 4,
USER1 = 5,
USER2 = 6,
TRACE = 7
};
[[nodiscard]] constexpr auto to_text(level l) -> std::string_view {
switch (l) {
case level::TRACE:
return "TRACE";
case level::INFO:
return "INFO";
case level::WARN:
return "WARN";
case level::ERROR:
return "ERROR";
case level::FATAL:
return "FATAL";
default:
return "UNKNOWN";
}
}
template <level L> struct level_constant : std::integral_constant<level, L> {};
} // namespace logging
namespace sc {
template <int value> constexpr static std::integral_constant<int, value> int_{};
template <unsigned int value>
constexpr static std::integral_constant<unsigned int, value> uint_{};
template <bool value>
constexpr static std::integral_constant<bool, value> bool_{};
template <char value>
constexpr static std::integral_constant<char, value> char_{};
template <auto enumValue>
constexpr static std::integral_constant<decltype(enumValue), enumValue> enum_{};
template <typename T> struct type_name {
constexpr explicit type_name(T) {}
constexpr type_name() = default;
};
template <typename T> constexpr static type_name<T> type_{};
template <typename CharT, CharT... chars> struct string_constant;
} // namespace sc
template <class T, T... chars>
constexpr auto operator""_sc() -> sc::string_constant<T, chars...> {
return {};
}
namespace sc {
template <typename StringConstant, typename ArgTuple>
struct lazy_string_format {
constexpr static StringConstant str{};
ArgTuple args{};
constexpr lazy_string_format() = default;
constexpr lazy_string_format(StringConstant, ArgTuple newArgs)
: args{newArgs} {}
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
template <typename F> constexpr auto apply(F &&f) const {
return args.apply(
[&](auto const &...as) { return std::forward<F>(f)(str, as...); });
}
};
template <class CharT, CharT... charsLhs, typename ArgsTupleLhs,
CharT... charsRhs, typename ArgsTupleRhs>
[[nodiscard]] constexpr auto operator==(
lazy_string_format<string_constant<CharT, charsLhs...>, ArgsTupleLhs> lhs,
lazy_string_format<string_constant<CharT, charsRhs...>, ArgsTupleRhs>
rhs) noexcept -> bool {
return (lhs.str == rhs.str) && (lhs.args == rhs.args);
}
template <typename StringConstantLhs, typename TupleArgsLhs,
typename StringConstantRhs, typename TupleArgsRhs>
[[nodiscard]] constexpr auto
operator+(lazy_string_format<StringConstantLhs, TupleArgsLhs> lhs,
lazy_string_format<StringConstantRhs, TupleArgsRhs> rhs) noexcept {
return lazy_string_format{lhs.str + rhs.str,
stdx::tuple_cat(lhs.args, rhs.args)};
}
template <typename StringConstantLhs, typename TupleArgsLhs, typename CharT,
CharT... chars>
[[nodiscard]] constexpr auto
operator+(lazy_string_format<StringConstantLhs, TupleArgsLhs> lhs,
string_constant<CharT, chars...> rhs) noexcept {
return lazy_string_format{lhs.str + rhs, lhs.args};
}
template <typename CharT, CharT... chars, typename StringConstantRhs,
typename TupleArgsRhs>
[[nodiscard]] constexpr auto
operator+(string_constant<CharT, chars...> lhs,
lazy_string_format<StringConstantRhs, TupleArgsRhs> rhs) noexcept {
return lazy_string_format{lhs + rhs.str, rhs.args};
}
} // namespace sc
namespace sc {
template <typename CharT, CharT... chars> struct string_constant {
private:
using view_t = std::basic_string_view<CharT>;
constexpr static std::array<CharT, sizeof...(chars)> storage{chars...};
using size_type = int;
using const_iterator = typename view_t::const_iterator;
constexpr static size_type npos = std::numeric_limits<size_type>::max();
public:
constexpr static view_t value{storage.data(), sizeof...(chars)};
constexpr static auto begin() noexcept { return std::cbegin(storage); }
constexpr static auto end() noexcept { return std::cend(storage); }
[[nodiscard]] constexpr static auto size() noexcept {
return std::size(storage);
}
template <size_type pos = 0, size_type count = npos>
[[nodiscard]] constexpr static auto
substr(std::integral_constant<size_type, pos>,
std::integral_constant<size_type, count> = {}) {
constexpr size_type sz = count == npos ? size() - pos : count;
return [&]<size_type... Is>(std::integer_sequence<size_type, Is...>) {
return string_constant<CharT, storage[pos + Is]...>{};
}(std::make_integer_sequence<size_type, sz>{});
}
template <typename F> constexpr auto apply(F &&f) const {
return std::forward<F>(f)(*this);
}
};
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator==(string_constant<CharT, charsLhs...>,
string_constant<CharT, charsRhs...>) noexcept -> bool {
return false;
}
template <class CharT, CharT... chars>
[[nodiscard]] constexpr auto
operator==(string_constant<CharT, chars...>,
string_constant<CharT, chars...>) noexcept -> bool {
return true;
}
#if __cpp_lib_three_way_comparison < 201907L
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator!=(string_constant<CharT, charsLhs...> lhs,
string_constant<CharT, charsRhs...> rhs) noexcept -> bool {
return not(lhs == rhs);
}
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator<(string_constant<CharT, charsLhs...> lhs,
string_constant<CharT, charsRhs...> rhs) noexcept -> bool {
return lhs.value < rhs.value;
}
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator>(string_constant<CharT, charsLhs...> lhs,
string_constant<CharT, charsRhs...> rhs) noexcept -> bool {
return lhs.value > rhs.value;
}
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator<=(string_constant<CharT, charsLhs...> lhs,
string_constant<CharT, charsRhs...> rhs) noexcept -> bool {
return lhs.value <= rhs.value;
}
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator>=(string_constant<CharT, charsLhs...> lhs,
string_constant<CharT, charsRhs...> rhs) noexcept -> bool {
return lhs.value >= rhs.value;
}
#else
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator<=>(string_constant<CharT, charsLhs...> lhs,
string_constant<CharT, charsRhs...> rhs) noexcept {
return lhs.value <=> rhs.value;
}
#endif
template <class CharT, CharT... charsLhs, CharT... charsRhs>
[[nodiscard]] constexpr auto
operator+(string_constant<CharT, charsLhs...>,
string_constant<CharT, charsRhs...>) noexcept
-> string_constant<CharT, charsLhs..., charsRhs...> {
return {};
}
} // namespace sc
namespace sc {
namespace detail {
template <typename T>
concept compile_time_field = requires { T::value; };
template <compile_time_field T> [[nodiscard]] CONSTEVAL auto field_value(T) {
if constexpr (std::is_enum_v<decltype(T::value)>) {
return stdx::enum_as_string<T::value>();
} else {
return T::value;
}
}
template <typename T>
[[nodiscard]] CONSTEVAL auto field_value(sc::type_name<T>) {
return stdx::type_as_string<T>();
}
template <typename Fmt, typename Arg> constexpr auto format1(Fmt, Arg arg) {
constexpr auto str = [&] {
constexpr auto fmtstr = FMT_COMPILE(Fmt::value);
constexpr auto sz = fmt::formatted_size(fmtstr, field_value(arg));
std::array<char, sz> buf{};
fmt::format_to(std::begin(buf), fmtstr, field_value(arg));
return buf;
}();
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return string_constant<char, str[Is]...>{};
}(std::make_index_sequence<std::size(str)>{});
}
template <typename Fmt> constexpr auto split_format_spec() {
constexpr Fmt fmt{};
constexpr auto spec_start = std::adjacent_find(
std::begin(fmt), std::end(fmt),
[](auto c1, auto c2) { return c1 == '{' and c2 != '{'; });
if constexpr (spec_start == std::end(fmt)) {
return std::pair{fmt, ""_sc};
} else {
constexpr auto spec_end = std::find_if(spec_start, std::end(fmt),
[](auto c) { return c == '}'; });
constexpr auto len = std::distance(std::begin(fmt), spec_end) + 1;
return std::pair{fmt.substr(int_<0>, int_<len>), fmt.substr(int_<len>)};
}
}
template <typename Str, typename Fmt, typename RuntimeTuple, typename Arg>
constexpr auto process_arg(stdx::tuple<Str, Fmt, RuntimeTuple> t, Arg arg) {
using namespace stdx::literals;
constexpr auto p = split_format_spec<Fmt>();
if constexpr (requires { field_value(arg); }) {
return stdx::make_tuple(t[0_idx] + format1(p.first, arg), p.second,
t[2_idx]);
} else if constexpr (requires { arg.args; }) {
return stdx::make_tuple(t[0_idx] + format1(p.first, arg.str), p.second,
stdx::tuple_cat(t[2_idx], arg.args));
} else {
return stdx::make_tuple(
t[0_idx] + p.first, p.second,
stdx::tuple_cat(t[2_idx], stdx::make_tuple(arg)));
}
}
} // namespace detail
template <typename Fmt, typename... Args>
constexpr auto format(Fmt, Args... args) {
using namespace stdx::literals;
auto t = stdx::make_tuple(args...);
auto r =
t.fold_left(stdx::make_tuple(""_sc, Fmt{}, stdx::tuple{}),
[](auto x, auto y) { return detail::process_arg(x, y); });
if constexpr (r[2_idx].size() == 0) {
return r[0_idx] + r[1_idx];
} else {
return lazy_string_format{r[0_idx] + r[1_idx], r[2_idx]};
}
}
template <typename T> struct formatter {
constexpr explicit formatter(T) {}
template <typename... Ts> constexpr auto operator()(Ts &&...args) {
return format(T{}, std::forward<Ts>(args)...);
}
};
} // namespace sc
namespace logging {
namespace null {
struct config {
struct {
template <level L>
constexpr auto log(auto &&...) const noexcept -> void {}
} logger;
};
} // namespace null
template <typename...> inline auto config = null::config{};
template <typename T>
concept loggable = requires(T const &t) {
t.apply([]<typename StringType>(StringType, auto const &...) {});
};
template <level L, typename... Ts, typename... TArgs>
static auto log(TArgs &&...args) -> void {
auto &cfg = config<Ts...>;
cfg.logger.template log<L>(std::forward<TArgs>(args)...);
}
} // namespace logging
#define CIB_LOG(LEVEL, MSG, ...) \
logging::log<LEVEL>(__FILE__, __LINE__, \
sc::formatter{MSG##_sc}(__VA_ARGS__))
#define CIB_TRACE(...) CIB_LOG(logging::level::TRACE, __VA_ARGS__)
#define CIB_INFO(...) CIB_LOG(logging::level::INFO, __VA_ARGS__)
#define CIB_WARN(...) CIB_LOG(logging::level::WARN, __VA_ARGS__)
#define CIB_ERROR(...) CIB_LOG(logging::level::ERROR, __VA_ARGS__)
#define CIB_FATAL(...) \
(CIB_LOG(logging::level::FATAL, __VA_ARGS__), STDX_PANIC(__VA_ARGS__))
#define CIB_ASSERT(expr) \
((expr) ? void(0) : CIB_FATAL("Assertion failure: " #expr))
namespace flow {
struct rt_node {
FunctionPtr run;
FunctionPtr log_name;
private:
friend constexpr auto operator==(rt_node const &, rt_node const &)
-> bool = default;
};
template <stdx::ct_string Name> struct ct_node : rt_node {
using is_node = void;
using name_t =
decltype(stdx::ct_string_to_type<Name, sc::string_constant>());
};
namespace detail {
template <stdx::ct_string Name, stdx::ct_string Type, typename F>
[[nodiscard]] constexpr auto make_node() {
return ct_node<Name>{
{.run = F{}, .log_name = [] {
CIB_TRACE("flow.{}({})",
stdx::ct_string_to_type<Type, sc::string_constant>(),
stdx::ct_string_to_type<Name, sc::string_constant>());
}}};
}
} // namespace detail
template <stdx::ct_string Name, typename F>
requires(stdx::is_function_object_v<F> and std::is_empty_v<F>)
[[nodiscard]] constexpr auto action(F const &) {
return detail::make_node<Name, "action", F>();
}
template <stdx::ct_string Name> [[nodiscard]] constexpr auto milestone() {
return detail::make_node<Name, "milestone", decltype([] {})>();
}
} // namespace flow
namespace flow {
// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)
struct interface {
virtual auto operator()() const -> void {}
};
/**
* flow::impl is a constant representation of a series of Milestones and actions
* to be executed in a specific order.
*
* flow::builder allows multiple independent components to collaboratively
* specify a flow::impl. Use flow::builder to create Flows. Independent
* components can then add their own actions and milestones to a flow::impl
* relative to other actions and milestones.
*
* @tparam Name
* Name of flow as a compile-time string.
*
* @tparam NumSteps
* The number of Milestones this flow::impl represents.
*
* @see flow::builder
*/
template <stdx::ct_string Name, std::size_t NumSteps>
class impl : public interface {
private:
constexpr static bool loggingEnabled = not Name.empty();
constexpr static auto capacity = [] {
if constexpr (loggingEnabled) {
return NumSteps * 2;
} else {
return NumSteps;
}
}();
stdx::cx_vector<FunctionPtr, capacity> functionPtrs{};
public:
using node_t = rt_node;
constexpr static bool active = capacity > 0;
/**
* Create a new flow::impl of Milestones.
*
* Do not call this constructor directly, use flow::builder instead.
*
* @param newMilestones
* Array of Milestones to execute in the flow.
*
* @see flow::builder
*/
constexpr explicit(true) impl(std::span<node_t const> newMilestones) {
CIB_ASSERT(NumSteps >= std::size(newMilestones));
if constexpr (loggingEnabled) {
for (auto const &milestone : newMilestones) {
functionPtrs.push_back(milestone.log_name);
functionPtrs.push_back(milestone.run);
}
} else {
std::transform(std::cbegin(newMilestones), std::cend(newMilestones),
std::back_inserter(functionPtrs),
[](auto const &milestone) { return milestone.run; });
}
}
/**
* Execute the entire flow in order.
*/
auto operator()() const -> void final {
constexpr auto name =
stdx::ct_string_to_type<Name, sc::string_constant>();
if constexpr (loggingEnabled) {
CIB_TRACE("flow.start({})", name);
}
for (auto const func : functionPtrs) {
func();
}
if constexpr (loggingEnabled) {
CIB_TRACE("flow.end({})", name);
}
}
};
} // namespace flow
namespace flow {
[[nodiscard]] constexpr auto edge_size(auto const &nodes, auto const &edges)
-> std::size_t {
auto const edge_capacities = transform(
[&]<typename N>(N const &) {
return edges.fold_left(
std::size_t{}, []<typename E>(auto acc, E const &) {
if constexpr (std::is_same_v<typename E::source_t, N> or
std::is_same_v<typename E::dest_t, N>) {
return ++acc;
} else {
return acc;
}
});
},
nodes);
return edge_capacities.fold_left(std::size_t{1}, [](auto acc, auto next) {
return std::max(acc, next);
});
}
template <template <stdx::ct_string, std::size_t> typename Impl>
struct graph_builder {
template <typename T> using name_for = typename T::name_t;
template <typename Node, std::size_t N, std::size_t E>
[[nodiscard]] constexpr static auto make_graph(auto const &nodes,
auto const &edges) {
using graph_t = stdx::cx_multimap<Node, Node, N, E>;
graph_t g{};
for_each([&](auto const &node) { g.put(node); }, nodes);
auto const named_nodes = stdx::apply_indices<name_for>(nodes);
for_each(
[&]<typename Edge>(Edge const &) {
g.put(get<name_for<typename Edge::source_t>>(named_nodes),
get<name_for<typename Edge::dest_t>>(named_nodes));
},
edges);
return g;
}
template <typename Node, typename Graph>
[[nodiscard]] constexpr static auto is_source_of(Node const &node,
Graph const &g) -> bool {
return std::find_if(g.begin(), g.end(), [&](auto const &entry) {
return entry.value.contains(node);
}) == g.end();
}
template <typename Graph>
[[nodiscard]] constexpr static auto get_sources(Graph const &g)
-> stdx::cx_set<typename Graph::key_type, Graph::capacity()> {
stdx::cx_set<typename Graph::key_type, Graph::capacity()> s;
for (auto const &entry : g) {
s.insert(entry.key);
}
for (auto const &entry : g) {
for (auto const &dst : entry.value) {
s.erase(dst);
}
}
return s;
}
template <typename Output, typename Graph>
[[nodiscard]] constexpr static auto topo_sort(Graph &g)
-> std::optional<Output> {
stdx::cx_vector<typename Graph::key_type, Graph::capacity()>
ordered_list{};
auto sources = get_sources(g);
while (not sources.empty()) {
auto n = sources.pop_back();
ordered_list.push_back(n);
if (g.contains(n)) {
auto ms = g.get(n);
if (ms.empty()) {
g.erase(n);
} else {
for (auto const &entry : ms) {
g.erase(n, entry);
if (is_source_of(entry, g)) {
sources.insert(entry);
}
}
}
}
}
if (not g.empty()) {
return {};
}
return std::optional<Output>{
std::in_place,
std::span{std::cbegin(ordered_list), std::size(ordered_list)}};
}
template <typename Graph>
[[nodiscard]] constexpr static auto build(Graph const &input) {
auto nodes = flow::dsl::get_nodes(input);
auto edges = flow::dsl::get_edges(input);
constexpr auto node_capacity = stdx::tuple_size_v<decltype(nodes)>;
constexpr auto edge_capacity = edge_size(nodes, edges);
using output_t = Impl<Graph::name, node_capacity>;
using rt_node_t = typename output_t::node_t;
static_assert(
all_of(
[]<typename N>(N const &) {
return std::is_convertible_v<N, rt_node_t>;
},
nodes),
"Output node type is not compatible with given input nodes");
auto g =
make_graph<rt_node_t, node_capacity, edge_capacity>(nodes, edges);
return topo_sort<output_t>(g);
}
template <typename Initialized> class built_flow {
constexpr static auto built() {
constexpr auto v = Initialized::value;
constexpr auto built = build(v);
static_assert(built.has_value());
return *built;
}
constexpr static auto run() { built()(); }
public:
// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator FunctionPtr() const { return run; }
constexpr auto operator()() const -> void { run(); }
constexpr static bool active = decltype(built())::active;
};
template <typename Initialized>
[[nodiscard]] constexpr static auto render() -> built_flow<Initialized> {
return {};
}
};
template <stdx::ct_string Name = "", typename Renderer = graph_builder<impl>,
flow::dsl::node... Fragments>
class graph {
friend constexpr auto tag_invoke(flow::dsl::get_nodes_t, graph const &g) {
auto t = g.fragments.apply([](auto const &...frags) {
return stdx::tuple_cat(flow::dsl::get_nodes(frags)...);
});
return stdx::to_unsorted_set(t);
}
friend constexpr auto tag_invoke(flow::dsl::get_edges_t, graph const &g) {
auto t = g.fragments.apply([](auto const &...frags) {
return stdx::tuple_cat(flow::dsl::get_edges(frags)...);
});
return stdx::to_unsorted_set(t);
}
public:
template <flow::dsl::node... Ns>
[[nodiscard]] constexpr auto add(Ns &&...ns) {
return fragments.apply([&](auto &...frags) {
return graph<Name, Renderer, Fragments...,
stdx::remove_cvref_t<Ns>...>{
{frags..., std::forward<Ns>(ns)...}};
});
}
template <typename BuilderValue>
[[nodiscard]] constexpr static auto build() {
return Renderer::template render<BuilderValue>();
}
constexpr static auto name = Name;
stdx::tuple<Fragments...> fragments;
};
} // namespace flow
namespace flow {
template <stdx::ct_string Name = "">
using builder = graph<Name, graph_builder<impl>>;
template <stdx::ct_string Name = "">
struct service : cib::builder_meta<builder<Name>, FunctionPtr> {};
} // namespace flow
namespace flow::dsl {
template <node Lhs, node Rhs> struct par {
Lhs lhs;
Rhs rhs;
using is_node = void;
private:
friend constexpr auto tag_invoke(get_initials_t, par const &p) {
return stdx::tuple_cat(get_initials(p.lhs), get_initials(p.rhs));
}
friend constexpr auto tag_invoke(get_finals_t, par const &p) {
return stdx::tuple_cat(get_finals(p.lhs), get_finals(p.rhs));
}
friend constexpr auto tag_invoke(get_nodes_t, par const &p) {
return stdx::tuple_cat(get_nodes(p.lhs), get_nodes(p.rhs));
}
friend constexpr auto tag_invoke(get_edges_t, par const &p) {
return stdx::tuple_cat(get_edges(p.lhs), get_edges(p.rhs));
}
};
template <node Lhs, node Rhs> par(Lhs, Rhs) -> par<Lhs, Rhs>;
} // namespace flow::dsl
template <flow::dsl::node Lhs, flow::dsl::node Rhs>
[[nodiscard]] constexpr auto operator&&(Lhs const &lhs, Rhs const &rhs) {
return flow::dsl::par{lhs, rhs};
}
namespace flow::dsl {
template <node Lhs, node Rhs> struct seq {
Lhs lhs;
Rhs rhs;
using is_node = void;
private:
friend constexpr auto tag_invoke(get_initials_t, seq const &s) {
return get_initials(s.lhs);
}
friend constexpr auto tag_invoke(get_finals_t, seq const &s) {
return get_finals(s.rhs);
}
friend constexpr auto tag_invoke(get_nodes_t, seq const &s) {
return stdx::tuple_cat(get_nodes(s.lhs), get_nodes(s.rhs));
}
friend constexpr auto tag_invoke(get_edges_t, seq const &s) {
auto is = get_initials(s.rhs);
auto fs = get_finals(s.lhs);
return stdx::tuple_cat(
get_edges(s.lhs), get_edges(s.rhs),
transform(
[]<typename P>(P const &) {
return edge<stdx::tuple_element_t<0, P>,
stdx::tuple_element_t<1, P>>{};
},
cartesian_product_copy(fs, is)));
}
};
template <node Lhs, node Rhs> seq(Lhs, Rhs) -> seq<Lhs, Rhs>;
} // namespace flow::dsl
template <flow::dsl::node Lhs, flow::dsl::node Rhs>
[[nodiscard]] constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) {
return flow::dsl::seq{lhs, rhs};
}
namespace flow {
/**
* Run the flow given by 'Tag'.
*
* @tparam Tag Type of the flow to be ran. This is the name of the flow::builder
* used to declare and build the flow.
*/
template <typename Tag> FunctionPtr &run = cib::service<Tag>;
} // namespace flow
namespace cib {
/**
* Executed immediately after the C++ runtime is stable. This should be
* used to initialize essential services like logging and potentially
* configure the host system the project is running on.
*/
class EarlyRuntimeInit : public flow::service<> {};
/**
* Executed once after essential services like logging are initialized.
* This can be used for general component runtime initialization.
*/
class RuntimeInit : public flow::service<> {};
/**
* Executed after all runtime initialization is completed. This is where
* the project can start doing its job including enabling interrupts and/or
* starting threads if applicable and be ready to start accepting and
* processing external events.
*/
class RuntimeStart : public flow::service<> {};
/**
* Executed repeated in an infinite loop after initialization and
* RuntimeStart flows have completed.
*/
class MainLoop : public flow::service<> {};
/**
* The top object for cib framework. Call 'main' to execute the project.
*/
template <typename ProjectConfig> class top {
private:
struct component {
constexpr static auto config = cib::config(
cib::exports<EarlyRuntimeInit, RuntimeInit, RuntimeStart, MainLoop>,
cib::components<ProjectConfig>);
};
constexpr static cib::nexus<component> my_nexus{};
public:
/**
* Main entry point to cib top.
*/
[[noreturn]] inline void main() {
my_nexus.init();
flow::run<EarlyRuntimeInit>();
CIB_INFO("cib::top::init() - RuntimeInit");
flow::run<RuntimeInit>();
CIB_INFO("cib::top::init() - RuntimeStart");
flow::run<RuntimeStart>();
while (true) {
flow::run<MainLoop>();
}
}
template <typename ServiceMeta>
constexpr static auto get_service() -> auto & {
return my_nexus.template service<ServiceMeta>;
}
};
} // namespace cib
namespace interrupt {
enum struct irq_num_t : std::uint32_t {};
using priority_t = std::size_t;
// NOLINTNEXTLINE(google-runtime-int)
CONSTEVAL auto operator""_irq(unsigned long long int v) -> irq_num_t {
return static_cast<irq_num_t>(v);
}
} // namespace interrupt
namespace interrupt {
template <typename T>
concept policy = requires { typename T::policy_type; };
template <typename T>
concept status_policy = policy<T> and requires(void (*f)()) {
{ T::run(f, f) } -> stdx::same_as<void>;
};
struct status_clear_policy;
struct clear_status_first {
using policy_type = status_clear_policy;
static void run(stdx::invocable auto const &clear_status,
stdx::invocable auto const &run) {
clear_status();
run();
}
static_assert(status_policy<clear_status_first>);
};
struct clear_status_last {
using policy_type = status_clear_policy;
static void run(stdx::invocable auto const &clear_status,
stdx::invocable auto const &run) {
run();
clear_status();
}
static_assert(status_policy<clear_status_last>);
};
struct dont_clear_status {
using policy_type = status_clear_policy;
static void run(stdx::invocable auto const &,
stdx::invocable auto const &run) {
run();
}
static_assert(status_policy<dont_clear_status>);
};
struct required_resources_policy;
template <typename... Resources> struct resource_list {};
template <typename T>
concept resources_policy =
policy<T> and
stdx::is_specialization_of_v<typename T::resources, resource_list>;
template <typename... Resources> struct required_resources {
using policy_type = required_resources_policy;
using resources = resource_list<Resources...>;
};
template <typename... Policies> struct policies {
template <typename PolicyType, typename Default>
constexpr static auto get() {
using M = stdx::type_map<
stdx::tt_pair<typename Policies::policy_type, Policies>...>;
return stdx::type_lookup_t<M, PolicyType, Default>{};
}
template <typename PolicyType, typename Default>
using type = decltype(get<PolicyType, Default>());
};
} // namespace interrupt
namespace interrupt {
namespace detail {
template <typename T, template <typename...> typename X>
concept specializes = stdx::is_specialization_of_v<std::remove_cvref_t<T>, X>;
}
template <typename T>
concept root_config = requires {
{ T::children } -> detail::specializes<stdx::tuple>;
{ T::descendants } -> detail::specializes<stdx::tuple>;
typename T::template dynamic_controller_t<int>;
};
template <typename T>
concept base_irq_config =
status_policy<typename T::status_policy_t> and
detail::specializes<typename T::resources_t, resource_list> and requires {
{ T::template enable<true>() } -> std::same_as<void>;
{ T::children } -> detail::specializes<stdx::tuple>;
{ T::descendants } -> detail::specializes<stdx::tuple>;
};
template <typename T>
concept irq_config = base_irq_config<T> and requires {
{ T::irq_number } -> std::same_as<irq_num_t const &>;
};
template <typename T>
concept sub_irq_config = base_irq_config<T> and requires {
T::enable_field;
T::status_field;
};
template <typename T>
concept base_irq_interface = requires(T const &t) {
{ t.get_interrupt_enables() } -> detail::specializes<stdx::tuple>;
{ t.run() } -> std::same_as<void>;
};
template <typename T>
concept irq_interface = base_irq_interface<T> and requires(T const &t) {
{ T::irq_number } -> std::same_as<irq_num_t const &>;
{ t.init_mcu_interrupts() } -> std::same_as<void>;
};
template <typename T>
concept sub_irq_interface = base_irq_interface<T>;
template <typename T, typename Flow>
concept nexus_for = requires {
T::template service<Flow>();
{ T::template service<Flow>.active } -> std::same_as<bool const &>;
};
} // namespace interrupt
namespace interrupt {
enum class resource_status { OFF = 0, ON = 1 };
template <typename Irq>
concept has_enable_field = requires { Irq::enable_field; };
template <typename Irq>
concept has_resource =
has_enable_field<Irq> and
not boost::mp11::mp_empty<typename Irq::resources_t>::value;
template <typename Root> struct dynamic_controller {
private:
using all_resources_t =
boost::mp11::mp_unique<decltype(Root::descendants.apply(
[]<typename... Irqs>(Irqs const &...) {
return boost::mp11::mp_append<typename Irqs::resources_t...>{};
}))>;
template <typename Register>
CONSTINIT static inline typename Register::DataType allowed_enables =
std::numeric_limits<typename Register::DataType>::max();
template <typename Register>
CONSTINIT static inline typename Register::DataType dynamic_enables{};
template <typename Resource>
CONSTINIT static inline bool is_resource_on = true;
template <typename Resource> struct doesnt_require_resource {
template <typename Irq>
using fn =
std::bool_constant<has_enable_field<Irq> and
not boost::mp11::mp_contains<
typename Irq::resources_t, Resource>::value>;
};
template <typename Register> struct in_register {
template <typename Field>
using fn = std::is_same<Register, typename Field::RegisterType>;
};
/**
* For each ResourceType, keep track of what interrupts can still be enabled
* when that resource goes down.
*
* Each bit in this mask corresponds to an interrupt enable field in
* RegType. If the bit is '1', that means the corresponding interrupt can be
* enabled when the resource is not available. If the bit is '0', that means
* the corresponding interrupt must be disabled when the resource is not
* available.
*
* @tparam ResourceType
* The resource we want to check.
*
* @tparam RegType
* The specific register mask we want to check.
*/
template <typename ResourceType, typename RegType>
constexpr static typename RegType::DataType irqs_allowed = []() {
// get all interrupt enable fields that don't require the given resource
auto const matching_irqs =
stdx::filter<doesnt_require_resource<ResourceType>::template fn>(
Root::descendants);
auto const interrupt_enables_tuple = stdx::transform(
[](auto irq) { return irq.enable_field; }, matching_irqs);
// filter fields that aren't in RegType
auto const fields_in_reg =
stdx::filter<in_register<RegType>::template fn>(
interrupt_enables_tuple);
// set the bits in the mask for interrupts that don't require the
// resource
using DataType = typename RegType::DataType;
return fields_in_reg.fold_left(
DataType{}, [](DataType value, auto field) -> DataType {
return value | field.get_mask();
});
}();
template <typename RegTypeTuple>
static inline void reprogram_interrupt_enables(RegTypeTuple regs) {
stdx::for_each(
[]<typename R>(R reg) {
// make sure we don't enable any interrupts that are not allowed
// according to resource availability
auto const final_enables =
allowed_enables<R> & dynamic_enables<R>;
// update the hardware registers
apply(write(reg.raw(final_enables)));
},
regs);
}
/**
* tuple of every interrupt register affected by a resource
*/
template <typename Irq>
using has_resource_t = std::bool_constant<has_resource<Irq>>;
constexpr static auto all_resource_affected_regs =
stdx::to_unsorted_set(stdx::transform(
[]<typename Irq>(Irq) { return Irq::enable_field.get_register(); },
stdx::filter<has_resource_t>(Root::descendants)));
/**
* Reprogram interrupt enables based on updated resource availability.
*/
static inline auto recalculate_allowed_enables() {
// set allowed_enables mask for each resource affected register
stdx::for_each(
[]<typename R>(R) {
using DataType = typename R::DataType;
allowed_enables<R> = std::numeric_limits<DataType>::max();
},
all_resource_affected_regs);
// for each resource, if it is not on, mask out unavailable interrupts
stdx::template_for_each<all_resources_t>([]<typename Rsrc>() {
if (not is_resource_on<Rsrc>) {
stdx::for_each(
[]<typename R>(R) {
allowed_enables<R> &= irqs_allowed<Rsrc, R>;
},
all_resource_affected_regs);
}
});
return all_resource_affected_regs;
}
/**
* Store the interrupt enable values that FW _wants_ at runtime,
* irrespective of any resource conflicts that would require specific
* interrupts to be disabled.
*
* @tparam RegType
* The croo::Register this value corresponds to.
*/
template <typename... Flows> struct match_flow {
template <typename Irq>
using fn =
std::bool_constant<has_enable_field<Irq> and
(... or Irq::template triggers_flow<Flows>)>;
};
template <bool Enable, typename... Flows>
static inline void enable_by_name() {
// NOTE: critical section is not needed here because shared state is
// only updated by the final call to enable_by_field
// TODO: add support to enable/disable top-level IRQs by name.
// this will require another way to manage them vs. mmio
// registers. once that goes in, then enable_by_field should be
// removed or made private.
auto const matching_irqs =
stdx::filter<match_flow<Flows...>::template fn>(Root::descendants);
auto const interrupt_enables_tuple = stdx::transform(
[](auto irq) { return irq.enable_field; }, matching_irqs);
interrupt_enables_tuple.apply([]<typename... Fields>(Fields...) {
enable_by_field<Enable, Fields...>();
});
}
template <typename ResourceType>
static inline void update_resource(resource_status status) {
conc::call_in_critical_section<dynamic_controller>([&] {
is_resource_on<ResourceType> = (status == resource_status::ON);
recalculate_allowed_enables();
reprogram_interrupt_enables(all_resource_affected_regs);
});
}
public:
template <typename ResourceType> static inline void turn_on_resource() {
update_resource<ResourceType>(resource_status::ON);
}
template <typename ResourceType> static inline void turn_off_resource() {
update_resource<ResourceType>(resource_status::OFF);
}
template <bool Enable, typename... Fields>
static inline void enable_by_field() {
conc::call_in_critical_section<dynamic_controller>([] {
[[maybe_unused]] const auto enable = []<typename F>() -> void {
using R = typename F::RegisterType;
if constexpr (Enable) {
dynamic_enables<R> |= F::get_mask();
} else {
dynamic_enables<R> &= ~F::get_mask();
}
};
(enable.template operator()<Fields>(), ...);
auto const unique_regs = stdx::to_unsorted_set(
stdx::tuple<typename Fields::RegisterType...>{});
reprogram_interrupt_enables(unique_regs);
});
}
template <typename... Flows> static inline void enable() {
enable_by_name<true, Flows...>();
}
template <typename... Flows> static inline void disable() {
enable_by_name<false, Flows...>();
}
};
} // namespace interrupt
namespace interrupt {
template <typename T>
concept hal_interface = requires(T const &t, void (*isr)()) {
{ t.init() } -> stdx::same_as<void>;
{
t.template irq_init<true, irq_num_t{}, std::size_t{}>()
} -> stdx::same_as<void>;
{
t.template run<dont_clear_status>(irq_num_t{}, isr)
} -> stdx::same_as<void>;
};
template <typename...> struct null_hal {
static auto init() -> void { undefined(); }
template <bool Enable, irq_num_t IrqNumber, std::size_t PriorityLevel>
static auto irq_init() -> void {
undefined();
}
template <status_policy>
static auto run(irq_num_t, stdx::invocable auto const &) -> void {
undefined();
}
private:
static auto undefined() -> void {
static_assert(stdx::always_false_v<null_hal>,
"No interrupt HAL defined: inject one");
}
};
static_assert(hal_interface<null_hal<>>);
template <typename...> inline auto injected_hal = null_hal{};
struct hal {
template <typename... Ts>
requires(sizeof...(Ts) == 0)
static auto init() -> void {
injected_hal<Ts...>.init();
}
template <bool Enable, irq_num_t IrqNumber, int Priority, typename... Ts>
requires(sizeof...(Ts) == 0)
static auto irq_init() -> void {
injected_hal<Ts...>.template irq_init<Enable, IrqNumber, Priority>();
}
template <status_policy P, typename... Ts>
requires(sizeof...(Ts) == 0)
static auto run(irq_num_t irq, stdx::invocable auto const &isr) -> void {
injected_hal<Ts...>.template run<P>(irq, isr);
}
};
static_assert(hal_interface<hal>);
} // namespace interrupt
namespace interrupt {
namespace detail {
template <typename Dynamic, irq_interface... Impls> struct manager {
void init() const {
// TODO: log exact interrupt manager configuration
// (should be a single compile-time string with no arguments)
hal::init();
init_mcu_interrupts();
init_sub_interrupts();
}
void init_mcu_interrupts() const { (Impls::init_mcu_interrupts(), ...); }
void init_sub_interrupts() const {
auto enables = stdx::tuple_cat(Impls::get_interrupt_enables()...);
enables.apply([]<typename... Enables>(Enables...) {
Dynamic::template enable_by_field<true, Enables...>();
});
}
template <irq_num_t Number> inline void run() const {
using M = stdx::type_map<stdx::vt_pair<Impls::irq_number, Impls>...>;
using irq_t = stdx::value_lookup_t<M, Number>;
if constexpr (not std::is_void_v<irq_t>) {
irq_t::run();
}
}
[[nodiscard]] constexpr auto max_irq() const -> irq_num_t {
return static_cast<irq_num_t>(
std::max({stdx::to_underlying(Impls::irq_number)...}));
}
};
template <typename Config> struct build_manager {
using dynamic_t = typename Config::template dynamic_controller_t<Config>;
template <typename... Built> using impl = manager<dynamic_t, Built...>;
};
} // namespace detail
template <interrupt::root_config Config, typename... Nexi>
using manager = typename Config::template build<
detail::build_manager<Config>::template impl, Nexi...>;
} // namespace interrupt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment