Skip to content

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
classscram::mef::ContainerElement <br>Elements in MEF containers.
classscram::mef::Attribute <br>MEF Element Attributes.
classscram::mef::Element <br>The MEF Element with attributes and a label.
classscram::mef::Role <br>Mixin class that manages private or public roles for elements as needed.
classscram::mef::Id <br>Mixin class for assigning unique identifiers to elements.
classscram::mef::TableRange <br>Wraps the element container tables into ranges of plain references to hide the memory smart or raw pointers.
classscram::mef::TableRange::iterator <br>The proxy forward iterator to extract values instead of pointers.
classscram::mef::Container <br>The MEF Container of unique elements.
classscram::mef::Composite <br>The composition of multiple mef::Containers.
classscram::mef::NodeMark <br>Mixin class for providing marks for graph nodes.
classscram::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::mef

Updated on 2025-11-11 at 16:51:08 +0000