text: //  Copyright (c) 2015 Anton Bikineev
  //  Copyright (c) 2014 Thomas Heller
  //  Copyright (c) 2022 Hartmut Kaiser
  //
  //  SPDX-License-Identifier: BSL-1.0
  //  Distributed under the Boost Software License, Version 1.0.
  //  See accompanying file LICENSE_1_0.txt or copy at
  //  http://www.boost.org/LICENSE_1_0.txt)
  
  #pragma once
  
  #include <hpx/config.hpp>
  #include <hpx/assert.hpp>
  #include <hpx/modules/errors.hpp>
  #include <hpx/preprocessor/stringize.hpp>
  #include <hpx/serialization/detail/polymorphic_intrusive_factory.hpp>
  #include <hpx/serialization/serialization_fwd.hpp>
  #include <hpx/serialization/traits/polymorphic_traits.hpp>
  #include <hpx/type_support/static.hpp>
  #include <hpx/type_support/unused.hpp>
  
  #include <cstdint>
  #include <map>
  #include <string>
  #include <type_traits>
  #include <vector>
  
  #include <hpx/config/warnings_prefix.hpp>
  
  namespace hpx::serialization::detail {
  
      class id_registry
      {
      public:
          HPX_NON_COPYABLE(id_registry);
  
      public:
          typedef void* (*ctor_t)();
          typedef std::map<std::string, ctor_t> typename_to_ctor_t;
          typedef std::map<std::string, std::uint32_t> typename_to_id_t;
          typedef std::vector<ctor_t> cache_t;
  
          static constexpr std::uint32_t invalid_id = ~0u;
  
          HPX_CORE_EXPORT void register_factory_function(
              const std::string& type_name, ctor_t ctor);
  
          HPX_CORE_EXPORT void register_typename(
              const std::string& type_name, std::uint32_t id);
  
          HPX_CORE_EXPORT void fill_missing_typenames();
  
          HPX_CORE_EXPORT std::uint32_t try_get_id(
              const std::string& type_name) const;
  
          std::uint32_t get_max_registered_id() const
          {
              return max_id;
          }
  
          HPX_CORE_EXPORT std::vector<std::string> get_unassigned_typenames()
              const;
  
          HPX_CORE_EXPORT static id_registry& instance();
  
      private:
          id_registry() noexcept
            : max_id(0u)
          {
          }
  
          friend struct ::hpx::util::static_<id_registry>;
          friend class polymorphic_id_factory;
  
          HPX_CORE_EXPORT void cache_id(std::uint32_t id, ctor_t ctor);
  
          std::uint32_t max_id;
          typename_to_ctor_t typename_to_ctor;
          typename_to_id_t typename_to_id;
          cache_t cache;
      };
  
      class polymorphic_id_factory
      {
      public:
          HPX_NON_COPYABLE(polymorphic_id_factory);
  
      private:
          typedef id_registry::ctor_t ctor_t;
          typedef id_registry::typename_to_ctor_t typename_to_ctor_t;
          typedef id_registry::typename_to_id_t typename_to_id_t;
          typedef id_registry::cache_t cache_t;
  
      public:
          template <class T>
          static T* create(std::uint32_t id, std::string const* name = nullptr)
          {
              const cache_t& vec = id_registry::instance().cache;
  
              if (id >= vec.size())    //-V104
              {
                  std::string msg(
                      "Unknown type descriptor " + std::to_string(id));
  #if defined(HPX_DEBUG)
                  if (name != nullptr)
                  {
                      msg += ", for typename " + *name + "\n";
                      msg += collect_registered_typenames();
                  }
  #else
                  HPX_UNUSED(name);
  #endif
                  HPX_THROW_EXCEPTION(
                      serialization_error, "polymorphic_id_factory::create", msg);
              }
  
              ctor_t ctor = vec[id];
              HPX_ASSERT(ctor != nullptr);    //-V108
              return static_cast<T*>(ctor());
          }
  
          HPX_CORE_EXPORT static std::uint32_t get_id(
              const std::string& type_name);
  
      private:
          polymorphic_id_factory() = default;
  
          HPX_CORE_EXPORT static polymorphic_id_factory& instance();
          HPX_CORE_EXPORT static std::string collect_registered_typenames();
  
          friend struct hpx::util::static_<polymorphic_id_factory>;
      };
  
      template <class T>
      struct register_class_name<T,
          typename std::enable_if<traits::is_serialized_with_id<T>::value>::type>
      {
          register_class_name()
          {
              id_registry::instance().register_factory_function(
                  T::hpx_serialization_get_name_impl(), &factory_function);
          }
  
          static void* factory_function()
          {
              return new T;
          }
  
          register_class_name& instantiate()
          {
              return *this;
          }
  
          static register_class_name instance;
      };
  
      template <class T>
      register_class_name<T,
          std::enable_if_t<traits::is_serialized_with_id<T>::value>>
          register_class_name<T,
              std::enable_if_t<traits::is_serialized_with_id<T>::value>>::
              instance;
  
      template <std::uint32_t desc>
      std::string get_constant_entry_name();
  
      template <std::uint32_t Id>
      struct add_constant_entry
      {
          add_constant_entry()
          {
              id_registry::instance().register_typename(
                  get_constant_entry_name<Id>(), Id);
          }
  
          static add_constant_entry instance;
      };
  
      template <std::uint32_t Id>
      add_constant_entry<Id> add_constant_entry<Id>::instance;
  
  }    // namespace hpx::serialization::detail
  
  #include <hpx/config/warnings_suffix.hpp>
  
  #define HPX_SERIALIZATION_ADD_CONSTANT_ENTRY(String, Id)                       \
      namespace hpx::serialization::detail {                                     \
          template <>                                                            \
          std::string get_constant_entry_name</**/ Id>()                         \
          {                                                                      \
              return HPX_PP_STRINGIZE(String);                                   \
          }                                                                      \
          template add_constant_entry<Id> add_constant_entry</**/ Id>::instance; \
      }                                                                          \
      /**/
  