subsys: insert xo/ into include path
This commit is contained in:
parent
5f025d8fc8
commit
2b195c47f7
2 changed files with 2 additions and 0 deletions
|
|
@ -1,303 +0,0 @@
|
|||
/* file Subsystem.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2022
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "indentlog/scope.hpp"
|
||||
#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*/
|
||||
|
||||
/* 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