Program Listing for File context.hpp¶
↰ Return to documentation for file (SeQuant/core/utility/context.hpp)
#ifndef SEQUANT_CORE_UTILITY_CONTEXT_HPP
#define SEQUANT_CORE_UTILITY_CONTEXT_HPP
#include <SeQuant/core/utility/macros.hpp>
#include <algorithm>
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
namespace sequant::detail {
template <typename Ctx>
inline Ctx& implicit_context_instance() {
static Ctx instance_;
return instance_;
}
struct ImplicitContextOverlay {
const void* key;
std::shared_ptr<const void> ctx;
};
using ImplicitContextOverlays = std::vector<ImplicitContextOverlay>;
inline ImplicitContextOverlays& implicit_context_overlays() {
thread_local ImplicitContextOverlays overlays;
return overlays;
}
template <typename Ctx>
const void* implicit_context_key() {
static const char key = 0;
return &key;
}
template <typename Ctx>
const Ctx* implicit_context_overlay() {
const auto& overlays = implicit_context_overlays();
for (auto it = overlays.rbegin(); it != overlays.rend(); ++it) {
if (it->key == implicit_context_key<Ctx>())
return static_cast<const Ctx*>(it->ctx.get());
}
return nullptr;
}
template <typename Ctx>
const Ctx& get_implicit_context() {
if (const auto* overlay = implicit_context_overlay<Ctx>()) return *overlay;
return implicit_context_instance<Ctx>();
}
template <typename Ctx>
void set_implicit_context(const Ctx& ctx) {
implicit_context_instance<Ctx>() = ctx;
}
template <typename Ctx>
void reset_implicit_context() {
implicit_context_instance<Ctx>() = Ctx{};
}
template <typename Ctx>
struct ImplicitContextResetter {
ImplicitContextResetter() = default;
explicit ImplicitContextResetter(const void* overlay) noexcept
: overlay_(overlay) {}
~ImplicitContextResetter() noexcept {
if (overlay_) {
auto& overlays = implicit_context_overlays();
const auto it = std::find_if(
overlays.rbegin(), overlays.rend(),
[this](const auto& o) { return o.ctx.get() == overlay_; });
SEQUANT_ASSERT(it != overlays.rend() && it == overlays.rbegin() &&
"scoped contexts must end in the reverse order of their "
"creation, on the thread that created them");
if (it != overlays.rend()) overlays.erase(std::next(it).base());
}
}
// neither copyable nor movable: a moved-from resetter would still hold
// overlay_ and pop the live scope; returning by value relies on guaranteed
// copy elision
ImplicitContextResetter(const ImplicitContextResetter&) = delete;
ImplicitContextResetter(ImplicitContextResetter&&) = delete;
ImplicitContextResetter& operator=(const ImplicitContextResetter&) = delete;
ImplicitContextResetter& operator=(ImplicitContextResetter&&) = delete;
private:
const void* overlay_ = nullptr;
};
template <typename Ctx>
ImplicitContextResetter<Ctx> set_scoped_implicit_context(Ctx ctx) {
auto overlay = std::make_shared<const Ctx>(std::move(ctx));
const void* overlay_ptr = overlay.get();
implicit_context_overlays().push_back(
{implicit_context_key<Ctx>(), std::move(overlay)});
return ImplicitContextResetter<Ctx>(overlay_ptr);
}
class ImplicitContextOverlaysScope {
public:
explicit ImplicitContextOverlaysScope(const ImplicitContextOverlays& overlays)
: active_(!overlays.empty() || !implicit_context_overlays().empty()) {
if (active_)
previous_ = std::exchange(implicit_context_overlays(), overlays);
}
~ImplicitContextOverlaysScope() {
if (active_) implicit_context_overlays() = std::move(previous_);
}
ImplicitContextOverlaysScope(const ImplicitContextOverlaysScope&) = delete;
ImplicitContextOverlaysScope& operator=(const ImplicitContextOverlaysScope&) =
delete;
private:
bool active_;
ImplicitContextOverlays previous_;
};
} // namespace sequant::detail
#endif // SEQUANT_CORE_UTILITY_CONTEXT_HPP