Add 'xo-subsys/' from commit '57eee82fa5'
git-subtree-dir: xo-subsys git-subtree-mainline:4fcb57bb8cgit-subtree-split:57eee82fa5
This commit is contained in:
commit
06981d4c29
7 changed files with 554 additions and 0 deletions
57
xo-subsys/.github/workflows/cmake-single-platform.yml
vendored
Normal file
57
xo-subsys/.github/workflows/cmake-single-platform.yml
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage.
|
||||||
|
# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml
|
||||||
|
name: CMake on a single platform
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
|
||||||
|
# You can convert this to a matrix build if you need cross-platform coverage.
|
||||||
|
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Clone xo-cmake
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: Rconybea/xo-cmake
|
||||||
|
path: repo/xo-cmake
|
||||||
|
|
||||||
|
- name: Configure xo-cmake
|
||||||
|
run: cmake -B ${{github.workspace}}/build_xo-cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/xo-cmake
|
||||||
|
|
||||||
|
- name: Build xo-cmake (trivial)
|
||||||
|
run: cmake --build ${{github.workspace}}/build_xo-cmake --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Install xo-cmake
|
||||||
|
run: cmake --install ${{github.workspace}}/build_xo-cmake
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
- name: Configure self (subsys)
|
||||||
|
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||||
|
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||||
|
run: cmake -B ${{github.workspace}}/build -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Build self (subsys)
|
||||||
|
# Build your program with the given configuration
|
||||||
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
|
- name: Test self (subsys)
|
||||||
|
working-directory: ${{github.workspace}}/build
|
||||||
|
# Execute tests defined by the CMake configuration.
|
||||||
|
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
|
||||||
|
run: ctest -C ${{env.BUILD_TYPE}}
|
||||||
8
xo-subsys/.gitignore
vendored
Normal file
8
xo-subsys/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# emacs workspace config
|
||||||
|
.projectile
|
||||||
|
# symlink to ${mybuilddirectory}/compile_commands.json for LSP
|
||||||
|
compile_commands.json
|
||||||
|
# LSP keeps state here
|
||||||
|
.cache
|
||||||
|
# typical build directories
|
||||||
|
.build*
|
||||||
38
xo-subsys/CMakeLists.txt
Normal file
38
xo-subsys/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# xo-subsys/CMakeLists.txt
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(subsys VERSION 0.1)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(cmake/xo-bootstrap-macros.cmake)
|
||||||
|
|
||||||
|
xo_cxx_toplevel_options2()
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# cmake -DCMAKE_BUILD_TYPE=coverage
|
||||||
|
xo_toplevel_coverage_config2()
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# cmake -DCMAKE_BUILD_TYPE=debug
|
||||||
|
xo_toplevel_debug_config2()
|
||||||
|
|
||||||
|
|
||||||
|
#set(XO_PROJECT_NAME subsys)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# installing header-only library
|
||||||
|
|
||||||
|
set(SELF_LIB subsys)
|
||||||
|
xo_add_headeronly_library(${SELF_LIB})
|
||||||
|
xo_install_library4(${SELF_LIB} ${PROJECT_NAME}Targets)
|
||||||
|
#add_library(subsys INTERFACE)
|
||||||
|
#xo_include_headeronly_options2(subsys)
|
||||||
|
#xo_install_library2(subsys)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# provide find_package() support
|
||||||
|
|
||||||
|
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||||
|
|
||||||
|
# end CMakeLists.txt
|
||||||
102
xo-subsys/README.md
Normal file
102
xo-subsys/README.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
# plugin initialization support
|
||||||
|
|
||||||
|
subsys is a small header-only library providing support for plugin initialization
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- provide application control of initialization order across c++ libraries
|
||||||
|
- circumvents the 'static order initialization fiasco'
|
||||||
|
- ensure initialization code runs exactly once if subsystem is linked
|
||||||
|
- enforce initialization order constraints
|
||||||
|
- defend against static linker stripping essential initialization code
|
||||||
|
- designed to work cleanly for libraries integrating into existing executable like python, java runtime, ..
|
||||||
|
- initialization state browseable at runtime
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### build + install `indentlog` dependency
|
||||||
|
|
||||||
|
see [github/rconybea/indentlog](https://github.com/Rconybea/indentlog)
|
||||||
|
|
||||||
|
### copy `subsys` repository locally
|
||||||
|
```
|
||||||
|
$ git clone git@github.com:rconybea/subsys.git
|
||||||
|
$ ls -d subsys
|
||||||
|
subsys
|
||||||
|
```
|
||||||
|
|
||||||
|
### build + install
|
||||||
|
```
|
||||||
|
$ cd subsys
|
||||||
|
$ mkdir build
|
||||||
|
$ cd build
|
||||||
|
$ INSTALL_PREFIX=/usr/local # or wherever you prefer
|
||||||
|
$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} ..
|
||||||
|
$ make
|
||||||
|
$ make install
|
||||||
|
```
|
||||||
|
|
||||||
|
`CMAKE_PREFIX_PATH` should point to prefix where `indentlog` is installed
|
||||||
|
|
||||||
|
alternatively, if you're a nix user:
|
||||||
|
```
|
||||||
|
$ git clone git@github.com:rconybea/xo-nix.git
|
||||||
|
$ ls -d xo-nix
|
||||||
|
xo-nix
|
||||||
|
$ cd xo-nix
|
||||||
|
$ nix-build -A subsys
|
||||||
|
```
|
||||||
|
|
||||||
|
### build for unit test coverage
|
||||||
|
```
|
||||||
|
$ cd subsys
|
||||||
|
$ mkdir ccov
|
||||||
|
$ cd ccov
|
||||||
|
$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug ..
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### 1
|
||||||
|
```
|
||||||
|
// initialization code in .hpp for a subsystem foo, that relies on related subsystem bar
|
||||||
|
|
||||||
|
#include "subsys/Subsystem.hpp"
|
||||||
|
|
||||||
|
enum S_foo_tag {}; /* tag to represent initialization of subsystem foo */
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct InitSubsys<S_foo_tag> {
|
||||||
|
static void init() {
|
||||||
|
// plugin initialization for subsystem foo
|
||||||
|
}
|
||||||
|
|
||||||
|
static InitEvidence require() {
|
||||||
|
InitEvidence retval;
|
||||||
|
|
||||||
|
// demand initialization of dependent subsystem bar,
|
||||||
|
// before initialization subsystem foo
|
||||||
|
//
|
||||||
|
retval ^= InitSubsys<S_bar_tag>::require();
|
||||||
|
|
||||||
|
// initialization of this subsystem foo
|
||||||
|
retval ^= Subsystem::provide<S_foo_tag>("foo", &init);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
// in application code that relies on foo (perhaps along with other subsystems),
|
||||||
|
// for example in a pybind11 module:
|
||||||
|
//
|
||||||
|
PYBIND11_MODULE(pyfoo, m) {
|
||||||
|
// include foo in initialization set
|
||||||
|
InitSubsys<S_foo_tag>::require();
|
||||||
|
// ensure foo + dependencies are initialized
|
||||||
|
Subsystem::initialize_all();
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
4
xo-subsys/cmake/subsysConfig.cmake.in
Normal file
4
xo-subsys/cmake/subsysConfig.cmake.in
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||||
|
check_required_components("@PROJECT_NAME@")
|
||||||
35
xo-subsys/cmake/xo-bootstrap-macros.cmake
Normal file
35
xo-subsys/cmake/xo-bootstrap-macros.cmake
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# for example:
|
||||||
|
# $ PREFIX=/usr/local # for example
|
||||||
|
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
|
||||||
|
#
|
||||||
|
# will get
|
||||||
|
# CMAKE_MODULE_PATH
|
||||||
|
# from xo-cmake-config --cmake-module-path
|
||||||
|
#
|
||||||
|
# and expect .cmake macros in
|
||||||
|
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
|
||||||
|
|
||||||
|
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
|
||||||
|
message(FATAL "could not find xo-cmake-config executable")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
|
||||||
|
|
||||||
|
if (NOT XO_SUBMODULE_BUILD)
|
||||||
|
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||||
|
# default to typical install location for xo-project-macros
|
||||||
|
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
|
||||||
|
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
|
||||||
|
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
|
||||||
|
#
|
||||||
|
include(xo_macros/xo_cxx)
|
||||||
|
|
||||||
|
xo_cxx_bootstrap_message()
|
||||||
310
xo-subsys/include/xo/subsys/Subsystem.hpp
Normal file
310
xo-subsys/include/xo/subsys/Subsystem.hpp
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
/* file Subsystem.hpp
|
||||||
|
*
|
||||||
|
* author: Roland Conybeare, Aug 2022
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "xo/indentlog/scope.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <functional>
|
||||||
|
#include <list>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
/* e.g. XO_SUBSYSTEM_TAG(simulator) => xo::S_simulator_tag */
|
||||||
|
#define XO_SUBSYSTEM_TAG(subsys_name) xo::S_ ## subsys_name ## _tag
|
||||||
|
|
||||||
|
/* e.g. XO_SUBSYSTEM_REQUIRE(simulator) =>
|
||||||
|
* xo::InitSubsys<xo::S_simulator_tag>::require()
|
||||||
|
*/
|
||||||
|
#define XO_SUBSYSTEM_REQUIRE(subsys_name) xo::InitSubsys<XO_SUBSYSTEM_TAG(subsys_name)>::require();
|
||||||
|
|
||||||
|
/* e.g. XO_SUBSYSTEM_PROVIDE(simulator, &init) =>
|
||||||
|
* xo::Subsystem::provide<xo::S_simulator_tag>("simulator", &init)
|
||||||
|
*/
|
||||||
|
#define XO_SUBSYSTEM_PROVIDE(subsys_name, init_addr) xo::Subsystem::provide<XO_SUBSYSTEM_TAG(subsys_name)>(STRINGIFY(subsys_name), init_addr)
|
||||||
|
|
||||||
|
//#define VERIFY_SUBSYSTEM(tag) Subsystem::verify_present<tag>(STRINGIFY(tag))
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
using xo::tostr;
|
||||||
|
|
||||||
|
/* evidence that one or more subsystems have been initialized.
|
||||||
|
* Used to prevent static linker stripping must-run initialization code
|
||||||
|
*/
|
||||||
|
class InitEvidence {
|
||||||
|
public:
|
||||||
|
InitEvidence() = default;
|
||||||
|
InitEvidence(std::uint64_t x) : evidence_{x} {}
|
||||||
|
|
||||||
|
std::uint64_t evidence() const { return evidence_; }
|
||||||
|
|
||||||
|
InitEvidence operator^=(InitEvidence x) {
|
||||||
|
this->evidence_ ^= x.evidence_;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
} /*operator^=*/
|
||||||
|
|
||||||
|
InitEvidence operator^(InitEvidence x) {
|
||||||
|
return InitEvidence(this->evidence_ ^ x.evidence_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* we don't care about the specific value computed here,
|
||||||
|
* purpose is to be sufficiently impenentrable to compiler such
|
||||||
|
* that static linker can't optimize it away
|
||||||
|
*/
|
||||||
|
std::uint64_t evidence_ = 0;
|
||||||
|
}; /*InitEvidence*/
|
||||||
|
|
||||||
|
inline std::ostream &
|
||||||
|
operator<<(std::ostream & os, InitEvidence x) {
|
||||||
|
os << "<init-evidence " << x.evidence() << ">";
|
||||||
|
return os;
|
||||||
|
} /*operator<<*/
|
||||||
|
|
||||||
|
/* Goals:
|
||||||
|
* 1. provide for code that must run once (and only once)
|
||||||
|
* to initialize subsystems
|
||||||
|
* 2. in executable, want to run such code after main() starts
|
||||||
|
* instead of relying on static initializers;
|
||||||
|
* that way init behavior can be parameterized based on
|
||||||
|
* program arguments
|
||||||
|
*
|
||||||
|
* Use
|
||||||
|
* // subsystem foo
|
||||||
|
*
|
||||||
|
* enum Foo_tag {};
|
||||||
|
*
|
||||||
|
* // guarantees that if anything gets initialized, then
|
||||||
|
* // foo_init() is included
|
||||||
|
* //
|
||||||
|
* template<>
|
||||||
|
* struct InitSubsys<Foo_tag> {
|
||||||
|
* static void foo_init() { ... }
|
||||||
|
*
|
||||||
|
* static InitEvidence require() {
|
||||||
|
* return Subsystem::require<Foo_tag>("foo", &foo_init);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* .. register other subsystems ..
|
||||||
|
*
|
||||||
|
* Subsystem::initialize_all(); // foo_init() has been called once
|
||||||
|
*
|
||||||
|
* If subsystem bar depends on supporting subsystem {foo, quux}, then write:
|
||||||
|
*
|
||||||
|
* enum Bar_tag {};
|
||||||
|
*
|
||||||
|
* template<>
|
||||||
|
* struct InitSubsys<Bar_tag> {
|
||||||
|
* static void bar_init() { ... }
|
||||||
|
*
|
||||||
|
* static InitEvidence require() {
|
||||||
|
* InitEvidence retval;
|
||||||
|
*
|
||||||
|
* retval ^= InitSubsys<Foo_tag>::require();
|
||||||
|
* retval ^= InitSubsys<Quux_tag>::require();
|
||||||
|
*
|
||||||
|
* retval ^= Subsystem::require<Bar_tag>("bar", &bar_init);
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* If using subsystems from a shared library (so no access to cmdline args etc):
|
||||||
|
* e.g. in pyfoo.cpp:
|
||||||
|
*
|
||||||
|
* InitEvidence s_pyfoo_init = InitSubsys<Foo_tag>::require();
|
||||||
|
* or
|
||||||
|
* InitEvidence s_pyfoo_init = (InitSubsys<Foo_tag>::require()
|
||||||
|
* ^ InitSubsys<Bar_tag>::require());
|
||||||
|
*
|
||||||
|
* Note: Tag argument here no relation of BuildTag in SubsystemImpl<BuildTag> below
|
||||||
|
*/
|
||||||
|
template<typename Tag>
|
||||||
|
struct InitSubsys {};
|
||||||
|
|
||||||
|
/* BuildTag: placeholder; insisting on header-only library */
|
||||||
|
template <typename BuildTag>
|
||||||
|
class SubsystemImpl {
|
||||||
|
public:
|
||||||
|
SubsystemImpl() = default;
|
||||||
|
SubsystemImpl(bool require_flag,
|
||||||
|
std::string_view subsys_name,
|
||||||
|
std::function<void ()> init_fn)
|
||||||
|
: require_flag_{require_flag},
|
||||||
|
subsys_name_{subsys_name},
|
||||||
|
init_fn_{init_fn} {}
|
||||||
|
|
||||||
|
/* establish an empty Subsystem record for subsys_name.
|
||||||
|
* record is _not_ linked into s_subsys_l!
|
||||||
|
* idempotent.
|
||||||
|
*/
|
||||||
|
template<typename SubsystemTag>
|
||||||
|
static SubsystemImpl * establish() {
|
||||||
|
static SubsystemImpl s_subsys;
|
||||||
|
|
||||||
|
return &s_subsys;
|
||||||
|
} /*establish*/
|
||||||
|
|
||||||
|
template<typename SubsystemTag>
|
||||||
|
static bool verify_present(std::string subsys_tag) {
|
||||||
|
SubsystemImpl * subsys = establish<SubsystemTag>();
|
||||||
|
|
||||||
|
if (!subsys->require_flag()) {
|
||||||
|
throw std::runtime_error(tostr("subsystem not present."
|
||||||
|
"(missing InitSubsys<", subsys_tag, ">::require() ?)"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} /*verify_present*/
|
||||||
|
|
||||||
|
/* provide (once only) initialization code for a subsystem with tag SubsystemTag.
|
||||||
|
* ideally this would be called just once for a particular tag;
|
||||||
|
* if called multiple times, calls after the first are no-ops.
|
||||||
|
*/
|
||||||
|
template<typename SubsystemTag>
|
||||||
|
static InitEvidence provide(std::string_view subsys_name,
|
||||||
|
std::function<void ()> init_fn) {
|
||||||
|
SubsystemImpl * subsys = establish<SubsystemTag>();
|
||||||
|
|
||||||
|
provide_aux(subsys_name, init_fn, subsys);
|
||||||
|
|
||||||
|
return InitEvidence(reinterpret_cast<std::uint64_t>(subsys));
|
||||||
|
} /*provide*/
|
||||||
|
|
||||||
|
/* throw exception if there's anything left for .initialize_all() to do,
|
||||||
|
* or subsystems have been added since last call to .initialize_all()
|
||||||
|
* Can use this to remind application author to call SubsystemImpl::initialize_all()
|
||||||
|
*/
|
||||||
|
static bool verify_all_initialized();
|
||||||
|
|
||||||
|
/* 1. initialize all subsystems: promise that for every preceding call
|
||||||
|
* to .require<tag>(), the corresponding initialization function has been
|
||||||
|
* run exactly once.
|
||||||
|
* 2. harmless to call this multiple times -- will not call any init_fn more than once
|
||||||
|
* 3. can interleave .initialize_all() with .require<tag>() as desired
|
||||||
|
*/
|
||||||
|
static InitEvidence initialize_all();
|
||||||
|
|
||||||
|
bool require_flag() const { return require_flag_; }
|
||||||
|
bool init_flag() const { return init_flag_; }
|
||||||
|
std::string_view subsys_name() const { return subsys_name_; }
|
||||||
|
|
||||||
|
InitEvidence initialize();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* helper for .provide<SubsystemTag>() */
|
||||||
|
static void provide_aux(std::string_view subsys_name,
|
||||||
|
std::function<void ()> init_fn,
|
||||||
|
SubsystemImpl * p_subsys);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* set to true iff .s_subsys_l has been extended since last call to .initialize_all() */
|
||||||
|
static bool s_dirty_flag;
|
||||||
|
/* one member for each unique call to .require() */
|
||||||
|
static std::list<SubsystemImpl *> s_subsys_l;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/* set to true on 1st call to .require() */
|
||||||
|
bool require_flag_ = false;
|
||||||
|
/* set to true when .init_fn() invoked */
|
||||||
|
bool init_flag_ = false;
|
||||||
|
/* unique subsystem name */
|
||||||
|
std::string_view subsys_name_;
|
||||||
|
/* call this function once (at most) to initialize this subsystem */
|
||||||
|
std::function<void ()> init_fn_;
|
||||||
|
}; /*SubsystemImpl*/
|
||||||
|
|
||||||
|
template <typename BuildTag>
|
||||||
|
bool
|
||||||
|
SubsystemImpl<BuildTag>::s_dirty_flag = false;
|
||||||
|
|
||||||
|
template <typename BuildTag>
|
||||||
|
std::list<SubsystemImpl<BuildTag> *>
|
||||||
|
SubsystemImpl<BuildTag>::s_subsys_l;
|
||||||
|
|
||||||
|
template <typename BuildTag>
|
||||||
|
void
|
||||||
|
SubsystemImpl<BuildTag>::provide_aux(std::string_view subsys_name,
|
||||||
|
std::function<void ()> init_fn,
|
||||||
|
SubsystemImpl<BuildTag> * p_subsys)
|
||||||
|
{
|
||||||
|
if (!p_subsys->require_flag()) {
|
||||||
|
/* 1st call to .provide() for this SubsystemTag */
|
||||||
|
|
||||||
|
using xo::scope;
|
||||||
|
using xo::xtag;
|
||||||
|
|
||||||
|
scope log(XO_ENTER0(chatty),
|
||||||
|
xtag("subsys", subsys_name),
|
||||||
|
xtag("address", p_subsys));
|
||||||
|
|
||||||
|
*p_subsys = SubsystemImpl<BuildTag>(true /*require_flag*/,
|
||||||
|
subsys_name,
|
||||||
|
init_fn);
|
||||||
|
|
||||||
|
s_dirty_flag = true;
|
||||||
|
s_subsys_l.push_back(p_subsys);
|
||||||
|
}
|
||||||
|
} /*provide_aux*/
|
||||||
|
|
||||||
|
template <typename BuildTag>
|
||||||
|
bool
|
||||||
|
SubsystemImpl<BuildTag>::verify_all_initialized()
|
||||||
|
{
|
||||||
|
if (s_dirty_flag) {
|
||||||
|
scope log(XO_ENTER0(error), "required subsystems NOT initialized!?");
|
||||||
|
|
||||||
|
for (SubsystemImpl<BuildTag> * subsys : s_subsys_l) {
|
||||||
|
if (!subsys->init_flag()) {
|
||||||
|
log && log("missing InitSubsys<S_", subsys->subsys_name(), "_tag>::require()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Subsystem::verify_initialized:"
|
||||||
|
" Subsystem::initialize_all() never called, or out-of-date");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} /*verify_all_initialized*/
|
||||||
|
|
||||||
|
template <typename BuildTag>
|
||||||
|
InitEvidence
|
||||||
|
SubsystemImpl<BuildTag>::initialize_all() {
|
||||||
|
scope log(XO_ENTER0(chatty));
|
||||||
|
|
||||||
|
InitEvidence retval;
|
||||||
|
|
||||||
|
if (s_dirty_flag) {
|
||||||
|
for (SubsystemImpl<BuildTag> * subsys : s_subsys_l) {
|
||||||
|
log && log("init", xtag("subsys", subsys->subsys_name()));
|
||||||
|
|
||||||
|
retval ^= subsys->initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_dirty_flag = false;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
} /*initialize_all*/
|
||||||
|
|
||||||
|
template <typename BuildTag>
|
||||||
|
InitEvidence
|
||||||
|
SubsystemImpl<BuildTag>::initialize()
|
||||||
|
{
|
||||||
|
if (!init_flag_) {
|
||||||
|
init_flag_ = true;
|
||||||
|
|
||||||
|
init_fn_();
|
||||||
|
}
|
||||||
|
|
||||||
|
return InitEvidence(reinterpret_cast<std::uint64_t>(this));
|
||||||
|
} /*initialize*/
|
||||||
|
|
||||||
|
using Subsystem = SubsystemImpl<class Subsystem_tag>;
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end Subsystem.hpp */
|
||||||
Loading…
Add table
Add a link
Reference in a new issue