Program Listing for File Duct.hpp

Return to documentation for file (include/uit/ducts/Duct.hpp)

#pragma once
#ifndef UIT_DUCTS_DUCT_HPP_INCLUDE
#define UIT_DUCTS_DUCT_HPP_INCLUDE

#include <optional>
#include <stddef.h>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>

#include "../../uit_emp/meta/TypePack.hpp"

#include "../../uitsl/containers/safe/unordered_map.hpp"
#include "../../uitsl/math/math_utils.hpp"
#include "../../uitsl/meta/HasMemberFunction.hpp"
#include "../../uitsl/mpi/mpi_init_utils.hpp"
#include "../../uitsl/parallel/thread_utils.hpp"
#include "../../uitsl/utility/print_utils.hpp"

namespace uit {
namespace internal {

UITSL_GENERATE_HAS_MEMBER_FUNCTION( CanStep );

template<typename ImplSpec>
class Duct {

  using ducts_t = typename uit_emp::TypePack<
    typename ImplSpec::IntraDuct,
    typename ImplSpec::ThreadDuct,
    typename ImplSpec::ProcInletDuct,
    typename ImplSpec::ProcOutletDuct
  >::make_unique;

  typename ducts_t::template apply<std::variant> impl;

  using T = typename ImplSpec::T;

  bool MaybeHoldsIntraImpl() const {
    return std::holds_alternative<typename ImplSpec::IntraDuct>( impl );
  }

  bool MaybeHoldsThreadImpl() const {
    return std::holds_alternative<typename ImplSpec::ThreadDuct>( impl );
  }

  bool MaybeHoldsProcImpl() const {
    return (
      std::holds_alternative<typename ImplSpec::ProcInletDuct>( impl )
      || std::holds_alternative<typename ImplSpec::ProcOutletDuct>( impl )
    );
  }

  bool HoldsAmbiguousImpl() const {
    return uitsl::sum(
      MaybeHoldsIntraImpl(),
      MaybeHoldsThreadImpl(),
      MaybeHoldsProcImpl()
    ) > 1;
  }

  using uid_t_ = std::uintptr_t;

  using t_registry_t = uitsl::safe::unordered_map<uid_t_, uitsl::thread_id_t>;
  inline static t_registry_t inlet_thread_registry;
  inline static t_registry_t outlet_thread_registry;

  using p_registry_t = uitsl::safe::unordered_map<uid_t_, uitsl::proc_id_t>;
  inline static p_registry_t inlet_proc_registry;
  inline static p_registry_t outlet_proc_registry;

  using edge_id_registry_t = uitsl::safe::unordered_map<uid_t_, size_t>;
  inline static edge_id_registry_t edge_id_registry;

  using node_id_registry_t = uitsl::safe::unordered_map<uid_t_, size_t>;
  inline static node_id_registry_t inlet_node_id_registry;
  inline static node_id_registry_t outlet_node_id_registry;

  using mesh_id_registry_t = uitsl::safe::unordered_map<uid_t_, size_t>;
  inline static mesh_id_registry_t mesh_id_registry;

public:

  using uid_t = uid_t_;

  Duct(Duct& other) = delete;

  Duct(const Duct& other) = delete;

  Duct(Duct&& other) = delete;

  template <typename... Args>
  Duct(Args&&... args)
  : impl(std::forward<Args>(args)...)
  { ; }

  template <typename WhichDuct, typename... Args>
  void EmplaceImpl(Args&&... args) {
    impl.template emplace<WhichDuct>(std::forward<Args>(args)...);
  }

  bool TryPut(const T& val) {
    return std::visit(
      [&val](auto& arg) -> bool { return arg.TryPut(val); },
      impl
    );
  }

  template<typename P>
  bool TryPut(P&& val) {
    return std::visit(
      [&val](auto& arg) -> bool { return arg.TryPut(std::forward<P>(val)); },
      impl
    );
  }

  bool TryFlush() {
    return std::visit(
      [](auto& arg) -> bool { return arg.TryFlush(); },
      impl
    );
  }

  const T& Get() const {
    return std::visit(
      [](auto& arg) -> const T& { return arg.Get(); },
      impl
    );
  }

  T& Get() {
    return std::visit(
      [](auto& arg) -> T& { return arg.Get(); },
      impl
    );
  }

  size_t TryConsumeGets(const size_t requested) {
    return std::visit(
      [requested](auto& arg) -> size_t {
        return arg.TryConsumeGets(requested);
      },
      impl
    );
  }

  std::string WhichImplIsActive() const {
    return std::visit(
      [](auto& arg) -> std::string { return arg.GetName(); },
      impl
    );
  }

  std::optional<bool> HoldsIntraImpl() const {
    if ( MaybeHoldsIntraImpl() ) {
      return HoldsAmbiguousImpl() ? std::nullopt : std::optional<bool>{ true };
    } else return false;
  }

  std::optional<bool> HoldsThreadImpl() const {
    if ( MaybeHoldsThreadImpl() ) {
      return HoldsAmbiguousImpl() ? std::nullopt : std::optional<bool>{ true };
    } else return false;
  }

  std::optional<bool> HoldsProcImpl() const {
    if ( MaybeHoldsProcImpl() ) {
      return HoldsAmbiguousImpl() ? std::nullopt : std::optional<bool>{ true };
    } else return false;
  }

  std::string WhichImplHeld() const {
    if ( HoldsIntraImpl().value_or(false) ) return "intra";
    else if ( HoldsThreadImpl().value_or(false) ) return "thread";
    else if ( HoldsProcImpl().value_or(false) ) return "proc";
    else return "ambiguous";
  }

  uid_t GetUID() const { return reinterpret_cast<uid_t>(this); }

  bool CanStep() const {
    return std::visit(
      [](const auto& arg) -> bool {
        using impl_t = typename std::decay<decltype(arg)>::type;
        if constexpr ( HasMemberFunction_CanStep<impl_t, bool()>::value ) {
          return impl_t::CanStep();
        } /* else */ // removed to silence no return from non-void warning
        return false;
      },
      impl
    );
  }

  void RegisterInletProc(const uitsl::proc_id_t proc) const {
    inlet_proc_registry[GetUID()] = proc;
  }

  void RegisterOutletProc(const uitsl::proc_id_t proc) const {
    outlet_proc_registry[GetUID()] = proc;
  }

  void RegisterInletThread(const uitsl::thread_id_t thread) const {
    inlet_thread_registry[GetUID()] = thread;
  }

  void RegisterOutletThread(const uitsl::thread_id_t thread) const {
    outlet_thread_registry[GetUID()] = thread;
  }

  void RegisterEdgeID(const size_t edge_id) const {
    edge_id_registry[GetUID()] = edge_id;
  }

  void RegisterInletNodeID(const size_t node_id) const {
    inlet_node_id_registry[GetUID()] = node_id;
  }

  void RegisterOutletNodeID(const size_t node_id) const {
    outlet_node_id_registry[GetUID()] = node_id;
  }

  void RegisterMeshID(const size_t mesh_id) const {
    mesh_id_registry[GetUID()] = mesh_id;
  }

  std::optional<uitsl::proc_id_t> LookupInletProc() const {
    return inlet_proc_registry.contains( GetUID() )
      ? std::optional<uitsl::proc_id_t>{ inlet_proc_registry.at( GetUID() ) }
      : std::nullopt
    ;
  }

  std::optional<uitsl::proc_id_t> LookupOutletProc() const {
    return outlet_proc_registry.contains( GetUID() )
      ? std::optional<uitsl::proc_id_t>{ outlet_proc_registry.at( GetUID() ) }
      : std::nullopt
    ;
  }

  std::optional<uitsl::thread_id_t> LookupInletThread() const {
    return inlet_thread_registry.contains( GetUID() )
      ? std::optional<uitsl::thread_id_t>{inlet_thread_registry.at( GetUID() )}
      : std::nullopt
    ;
  }

  std::optional<uitsl::thread_id_t> LookupOutletThread() const {
    return outlet_thread_registry.contains( GetUID() )
      ? std::optional<uitsl::thread_id_t>{outlet_thread_registry.at( GetUID() )}
      : std::nullopt
    ;
  }

  std::optional<size_t> LookupEdgeID() const {
    return edge_id_registry.contains( GetUID() )
      ? std::optional<size_t>{edge_id_registry.at( GetUID() )}
      : std::nullopt
    ;
  }

  std::optional<size_t> LookupInletNodeID() const {
    return inlet_node_id_registry.contains( GetUID() )
      ? std::optional<size_t>{inlet_node_id_registry.at(GetUID())}
      : std::nullopt
    ;
  }

  std::optional<size_t> LookupOutletNodeID() const {
    return outlet_node_id_registry.contains( GetUID() )
      ? std::optional<size_t>{outlet_node_id_registry.at(GetUID())}
      : std::nullopt
    ;
  }

  std::optional<size_t> LookupMeshID() const {
    return mesh_id_registry.contains( GetUID() )
      ? std::optional<size_t>{mesh_id_registry.at(GetUID())}
      : std::nullopt
    ;
  }

  std::string ToString() const {
    std::stringstream ss;
    ss << uitsl::format_member(
      "uitsl::get_proc_id()",
      uitsl::get_proc_id()
    ) << '\n';
    ss << uitsl::format_member(
      "GetUID()",
      GetUID()
    ) << '\n';
    ss << uitsl::format_member(
      "std::variant impl",
      std::visit(
        [](auto& arg) -> std::string { return arg.ToString(); },
        impl
      )
    );
    return ss.str();
  }

};

} // namespace internal
} // namespace uit

#endif // #ifndef UIT_DUCTS_DUCT_HPP_INCLUDE