packages/engine/scram-node/src/element.h
Base/mixin classes, structs, and properties common to all MEF classes/constructs.
Namespaces
| Name |
|---|
| scram |
| scram::mef |
| scram::mef::detail |
Classes
| Name | |
|---|---|
| class | scram::mef::ContainerElement <br>Elements in MEF containers. |
| class | scram::mef::Attribute <br>MEF Element Attributes. |
| class | scram::mef::Element <br>The MEF Element with attributes and a label. |
| class | scram::mef::Role <br>Mixin class that manages private or public roles for elements as needed. |
| class | scram::mef::Id <br>Mixin class for assigning unique identifiers to elements. |
| class | scram::mef::TableRange <br>Wraps the element container tables into ranges of plain references to hide the memory smart or raw pointers. |
| class | scram::mef::TableRange::iterator <br>The proxy forward iterator to extract values instead of pointers. |
| class | scram::mef::Container <br>The MEF Container of unique elements. |
| class | scram::mef::Composite <br>The composition of multiple mef::Containers. |
| class | scram::mef::NodeMark <br>Mixin class for providing marks for graph nodes. |
| class | scram::mef::Usage <br>Mixin class for providing usage marks for elements. |
Source code
cpp
/*
* Copyright (C) 2014-2018 Olzhas Rakhimov
* Copyright (C) 2023 OpenPRA ORG Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/noncopyable.hpp>
#include "error.h"
#include "ext/linear_set.h"
#include "ext/multi_index.h"
namespace scram::mef {
class Element;
class ContainerElement {
template <class, class, bool, bool>
friend class Container; // Only containers manage their elements.
protected:
const Element* container() const { return container_; }
private:
void container(const Element* element) { container_ = element; }
const Element* container_ = nullptr;
};
class Attribute {
public:
Attribute(std::string name, std::string value, std::string type = "") {
Attribute::name(std::move(name));
Attribute::value(std::move(value));
Attribute::type(std::move(type));
}
const std::string& name() const { return name_; }
void name(std::string name) {
if (name.empty())
SCRAM_THROW(LogicError("Attribute name cannot be empty."));
name_ = std::move(name);
}
const std::string& value() const { return value_; }
void value(std::string value) {
if (value.empty())
SCRAM_THROW(LogicError("Attribute value cannot be empty."));
value_ = std::move(value);
}
const std::string& type() const { return type_; }
void type(std::string type) { type_ = std::move(type); }
private:
std::string name_;
std::string value_;
std::string type_;
};
class Element : public ContainerElement, private boost::noncopyable {
struct AttributeKey {
std::string_view operator()(const Attribute& attribute) const {
return attribute.name();
}
};
public:
using AttributeMap = ext::linear_set<Attribute, AttributeKey>;
explicit Element(std::string name);
const std::string& name() const { return name_; }
std::string_view name_view() const { return name_; }
const std::string& label() const { return label_; }
void label(std::string label) { label_ = std::move(label); }
const AttributeMap& attributes() const { return attributes_; }
void AddAttribute(Attribute attr);
void SetAttribute(Attribute attr) noexcept;
const Attribute* GetAttribute(std::string_view name) const noexcept;
std::optional<Attribute> RemoveAttribute(std::string_view name) noexcept;
protected:
~Element() = default;
void name(std::string name);
private:
std::string name_;
std::string label_;
AttributeMap attributes_;
};
template <typename T>
using ElementTable = boost::multi_index_container<
T, boost::multi_index::indexed_by<boost::multi_index::hashed_unique<
boost::multi_index::const_mem_fun<Element, std::string_view,
&Element::name_view>,
std::hash<std::string_view>>>>;
enum class RoleSpecifier : std::uint8_t { kPublic, kPrivate };
class Role {
public:
explicit Role(RoleSpecifier role = RoleSpecifier::kPublic,
std::string base_path = "");
RoleSpecifier role() const { return kRole_; }
const std::string& base_path() const { return kBasePath_; }
protected:
~Role() = default;
private:
const std::string kBasePath_;
const RoleSpecifier kRole_;
};
template <typename T>
std::string GetFullPath(const T* element) {
return element->base_path() + "." + element->name();
}
class Id : public Element, public Role {
public:
explicit Id(std::string name, std::string base_path = "",
RoleSpecifier role = RoleSpecifier::kPublic);
const std::string& id() const {
return Role::role() == RoleSpecifier::kPublic ? Element::name()
: full_path_;
}
std::string_view id_view() const { return id(); }
std::string_view full_path() const { return full_path_; }
void id(std::string name);
static const std::string& unique_name(const Element& element) {
return element.name();
}
static const std::string& unique_name(const Id& element) {
return element.id();
}
protected:
~Id() = default;
private:
std::string full_path_;
};
template <typename T>
using IdTable = boost::multi_index_container<
T,
boost::multi_index::indexed_by<boost::multi_index::hashed_unique<
boost::multi_index::const_mem_fun<Id, std::string_view, &Id::id_view>,
std::hash<std::string_view>>>>;
template <class T>
class TableRange {
using deref_type = std::decay_t<decltype(*typename T::value_type(nullptr))>;
using key_type = typename T::key_type;
public:
using value_type =
std::conditional_t<std::is_const_v<T>, std::add_const_t<deref_type>,
deref_type>;
using reference = value_type&;
using pointer = value_type*;
static_assert(std::is_const_v<T> == std::is_const_v<value_type>);
class iterator : public boost::iterator_facade<iterator, value_type,
boost::forward_traversal_tag> {
friend class boost::iterator_core_access;
public:
/*explicit*/ iterator(typename T::const_iterator it) : it_(it) {} // NOLINT
private:
void increment() { ++it_; }
bool equal(const iterator& other) const { return it_ == other.it_; }
reference dereference() const { return **it_; }
typename T::const_iterator it_;
};
using const_iterator = iterator;
explicit TableRange(T& table) : table_(table) {}
bool empty() const { return table_.empty(); }
std::size_t size() const { return table_.size(); }
std::size_t count(const key_type& key) const { return table_.count(key); }
iterator find(const key_type& key) const { return table_.find(key); }
iterator begin() const { return table_.begin(); }
iterator end() const { return table_.end(); }
iterator cbegin() const { return table_.begin(); }
iterator cend() const { return table_.end(); }
private:
T& table_;
};
template <class Self, class T, bool Ownership = true,
bool ById = std::is_base_of_v<Id, T>>
class Container {
public:
using ElementType = T;
using Pointer = std::conditional_t<Ownership, std::unique_ptr<T>, T*>;
using TableType =
std::conditional_t<ById, IdTable<Pointer>, ElementTable<Pointer>>;
using key_type = typename TableType::key_type;
auto table() const { return TableRange(table_); }
auto table() { return TableRange(table_); }
const T& Get(const key_type& id) const {
auto it = table_.find(id);
if (it != table_.end())
return **it;
SCRAM_THROW(UndefinedElement())
<< errinfo_element(std::string(id), T::kTypeString)
<< errinfo_container(Id::unique_name(static_cast<const Self&>(*this)),
Self::kTypeString);
}
T& Get(const key_type& id) {
return const_cast<T&>(std::as_const(*this).Get(id));
}
void Add(Pointer element) {
T& stable_ref = *element; // The pointer will be moved later.
if (table_.insert(std::move(element)).second == false) {
SCRAM_THROW(DuplicateElementError())
<< errinfo_element(Id::unique_name(stable_ref), T::kTypeString)
<< errinfo_container(Id::unique_name(static_cast<Self&>(*this)),
Self::kTypeString);
}
stable_ref.container(static_cast<const Self*>(this));
}
Pointer Remove(T* element) {
const std::string& key = [element]() -> decltype(auto) {
if constexpr (ById) {
return element->id();
} else {
return element->name();
}
}();
auto it = table_.find(key);
try {
if (it == table_.end())
SCRAM_THROW(UndefinedElement());
if (&**it != element)
SCRAM_THROW(LogicError("Duplicate element with different address."));
} catch (Error& err) {
err << errinfo_element(Id::unique_name(*element), T::kTypeString)
<< errinfo_container(Id::unique_name(static_cast<const Self&>(*this)),
Self::kTypeString);
throw;
}
element->container(nullptr);
return ext::extract(it, &table_); // no-throw.
}
protected:
const TableType& data() const { return table_; }
private:
TableType table_;
};
namespace detail { // Composite container helper facilities.
template <class E, class T, class... Ts>
struct container_of_impl {
using type = std::conditional_t<std::is_same_v<E, typename T::ElementType>, T,
typename container_of_impl<E, Ts...>::type>;
};
template <class E>
struct container_of_impl<E, void> {
using type = void;
};
template <class T, class... Ts>
struct container_of {
static_assert(std::is_base_of_v<Element, T>);
using type = typename container_of_impl<T, Ts..., void>::type;
static_assert(!std::is_same_v<type, void>,
"No container with elements of type T.");
};
} // namespace detail
template <typename... Ts>
class Composite : public Ts... {
public:
using Ts::Add...;
using Ts::Remove...;
template <class T>
auto table() const {
using ContainerType = typename detail::container_of<T, Ts...>::type;
return ContainerType::table();
}
template <class T>
auto table() {
using ContainerType = typename detail::container_of<T, Ts...>::type;
return ContainerType::table();
}
template <class T,
class ContainerType = typename detail::container_of<T, Ts...>::type>
const T& Get(const typename ContainerType::key_type& id) const {
return ContainerType::Get(id);
}
template <class T,
class ContainerType = typename detail::container_of<T, Ts...>::type>
T& Get(const typename ContainerType::key_type& id) {
return ContainerType::Get(id);
}
protected:
template <class T>
decltype(auto) data() const {
using ContainerType = typename detail::container_of<T, Ts...>::type;
return ContainerType::data();
}
};
template <class T, class... Ts>
using MultiContainer = Composite<Container<T, Ts>...>;
class NodeMark {
public:
enum Mark : std::uint8_t {
kClear = 0,
kTemporary,
kPermanent
};
Mark mark() const { return mark_; }
void mark(Mark label) { mark_ = label; }
protected:
~NodeMark() = default;
private:
Mark mark_ = kClear;
};
class Usage {
public:
bool usage() const { return usage_; }
void usage(bool usage) { usage_ = usage; }
protected:
~Usage() = default;
private:
bool usage_ = false;
};
} // namespace scram::mefUpdated on 2025-11-11 at 16:51:08 +0000
