Program Listing for File object_factory.h

Return to documentation for file (framework/object_factory.h)

// SPDX-FileCopyrightText: 2024 The OpenSn Authors <https://open-sn.github.io/opensn/>
// SPDX-License-Identifier: MIT

#pragma once

#include "framework/parameters/input_parameters.h"
#include "framework/logging/log_exceptions.h"
#include "framework/utils/utils.h"
#include <memory>

/**
 * Macro for registering an object within the ObjectFactory singleton.
 * \param namespace_name Name of the namespace within which the object is.
 * \param object_name Name of the object in the registry.
 * Example:
 * \code
 * OpenSnRegisterObjectInNamespace(kaka, Zorba);
 * \endcode
 * \note Remember to include the header "framework/object_factory.h".*/
#define OpenSnRegisterObjectInNamespace(namespace_name, object_name)                               \
  static char OpenSnJoinWords(unique_var_name_object_##object_name##_, __COUNTER__) =              \
    opensn::ObjectFactory::AddObjectToRegistry<object_name>(#namespace_name, #object_name)

#define OpenSnRegisterObject(object_name)                                                          \
  static char OpenSnJoinWords(unique_var_name_object_##object_name##_, __COUNTER__) =              \
    opensn::ObjectFactory::AddObjectToRegistry<object_name>(#object_name)

/**
 * Macro for registering an object alias within the ObjectFactory
 *
 * \param namespace_name Namespace name
 * \param alias Name of the object
 * \param object_name C++ class name to register.
 *
 * \note This will register a C++ class `object_name` such that it will show up as
 * `namespace_name.alias`
 */
#define OpenSnRegisterObjectAliasInNamespace(namespace_name, alias, object_name)                   \
  static char OpenSnJoinWords(unique_var_name_object_##object_name##_, __COUNTER__) =              \
    opensn::ObjectFactory::AddObjectToRegistry<object_name>(#namespace_name, #object_name)

/**
 * Macro for registering an object (parameters only) within the
 * ObjectFactory singleton.
 * \param namespace_name Name of the namespace within which the object is.
 * \param object_name Name of the object in the registry.
 * Example:
 * \code
 * OpenSnRegisterObjectParametersOnlyInNamespace(kaka, Zorba);
 * \endcode
 *
 * \note Remember to include the header "framework/object_factory.h"*/
#define OpenSnRegisterObjectParametersOnlyInNamespace(namespace_name, object_name)                 \
  static char OpenSnJoinWords(unique_var_name_object_##object_name##_, __COUNTER__) =              \
    opensn::ObjectFactory::AddObjectToRegistryParamsOnly<object_name>(#namespace_name,             \
                                                                      #object_name)

#define OpenSnRegisterObjectParametersOnly(object_name)                                            \
  static char OpenSnJoinWords(unique_var_name_object_##object_name##_, __COUNTER__) =              \
    opensn::ObjectFactory::AddObjectToRegistryParamsOnly<object_name>(#object_name)

namespace opensn
{

/// Singleton object for handling the registration and making of `Object`s.
class ObjectFactory
{
public:
  using ObjectGetInParamsFunc = InputParameters (*)();

  /// Structure storing the entities necessary for creating an object
  struct ObjectRegistryEntry
  {
    ObjectGetInParamsFunc get_in_params_func = nullptr;
  };

  // Deleted copy, move constructors and copy assignment operator
  ObjectFactory(const ObjectFactory&) = delete;
  ObjectFactory(const ObjectFactory&&) = delete;
  ObjectFactory& operator=(const ObjectFactory&) = delete;

  /// Access to the singleton
  static ObjectFactory& GetInstance() noexcept;

  /// Returns a constant reference to the object registry.
  const std::map<std::string, ObjectRegistryEntry>& GetRegistry() const;

  /// Checks if the object registry has a specific text key.
  bool RegistryHasKey(const std::string& key) const;

  template <typename T>
  static char AddObjectToRegistry(const std::string& namespace_name, const std::string& object_name)
  {
    return AddObjectToRegistry<T>(namespace_name + "::" + object_name);
  }

  template <typename T>
  static char AddObjectToRegistry(const std::string& object_name)
  {
    auto& object_maker = GetInstance();

    object_maker.AssertRegistryKeyAvailable(object_name, __PRETTY_FUNCTION__);

    ObjectRegistryEntry reg_entry;
    reg_entry.get_in_params_func = &CallGetInputParamsFunction<T>;
    object_maker.object_registry_.insert(std::make_pair(object_name, reg_entry));

    return 0;
  }

  template <typename T>
  static char AddObjectToRegistryParamsOnly(const std::string& namespace_name,
                                            const std::string& object_name)
  {
    return AddObjectToRegistryParamsOnly<T>(namespace_name + "::" + object_name);
  }

  template <typename T>
  static char AddObjectToRegistryParamsOnly(const std::string& object_name)
  {
    auto& object_maker = GetInstance();

    object_maker.AssertRegistryKeyAvailable(object_name, __PRETTY_FUNCTION__);

    ObjectRegistryEntry reg_entry;
    reg_entry.get_in_params_func = &CallGetInputParamsFunction<T>;
    object_maker.object_registry_.insert(std::make_pair(object_name, reg_entry));

    return 0;
  }

  static char AddSyntaxBlockToRegistry(const std::string& namespace_name,
                                       const std::string& block_name,
                                       ObjectGetInParamsFunc syntax_function)
  {
    return AddSyntaxBlockToRegistry(namespace_name + "::" + block_name, syntax_function);
  }

  static char AddSyntaxBlockToRegistry(const std::string& block_name,
                                       ObjectGetInParamsFunc syntax_function)
  {
    auto& object_maker = GetInstance();

    object_maker.AssertRegistryKeyAvailable(block_name, __PRETTY_FUNCTION__);

    ObjectRegistryEntry reg_entry;
    reg_entry.get_in_params_func = syntax_function;
    object_maker.object_registry_.insert(std::make_pair(block_name, reg_entry));

    return 0;
  }

  template <class TYPE>
  std::shared_ptr<TYPE> Create(const std::string& type, const ParameterBlock& params) const
  {
    if (object_registry_.count(type) == 0)
      throw std::logic_error("No registered type \"" + type + "\" found.");

    auto object_entry = object_registry_.at(type);
    if (not object_entry.get_in_params_func)
      throw std::runtime_error(
        "Object is not constructable since it has no registered constructor");

    auto input_params = object_entry.get_in_params_func();
    input_params.SetObjectType(type);
    input_params.SetErrorOriginScope(type);
    input_params.AssignParameters(params);
    auto obj = std::make_shared<TYPE>(input_params);
    return obj;
  }

  /// Returns the input parameters of a registered object.
  InputParameters GetRegisteredObjectParameters(const std::string& type) const;

  /// Dumps the object registry to stdout.
  void DumpRegister() const;

private:
  std::map<std::string, ObjectRegistryEntry> object_registry_;

  /// Private constructor because this is a singleton.
  ObjectFactory() = default;

  /// Utility redirection to call an object's static `GetInputParameters` function.
  template <typename T>
  static InputParameters CallGetInputParamsFunction()
  {
    return T::GetInputParameters();
  }

  /// Utility redirection to call an object's constructor with a specified list of input parameters.
  template <typename T, typename base_T>
  static std::shared_ptr<base_T> CallObjectConstructor(const InputParameters& params)
  {
    static_assert(std::is_base_of_v<base_T, T>, "Is not a base");
    return std::make_shared<T>(params);
  }

  /// Checks that the registry key is available and throws a `std::logical_error` if it is not.
  void AssertRegistryKeyAvailable(const std::string& key,
                                  const std::string& calling_function) const;
};

} // namespace opensn