Newer
Older
ForwardPlusRenderer / include / ConstrainedMap.h
#pragma once
#include <stdafx.h>
namespace fpr
{

/**
 * @brief Requires the given type to have a .get() function. Could be improved, such as requiring that .get() returns
 * std::same_as<decltype(*std::declval<T>())> though this is not valid for all types so must be evaluated further.
 */
template<typename T>
concept unique_handle = requires
{
  { std::declval<T>().get() };
};


template<typename T>
struct unique_handle_type
{
  using type = T;
};

// Requires a type to have a ::element_type alias.
template<typename T>
requires unique_handle<T>
struct unique_handle_type<T>
{
  using type = T::element_type;
};

// Not every type has a pointer that is functionally equivalent, i.e file handles.
template<typename Elem, typename Deleter>
struct unique_handle_type<std::unique_ptr<Elem, Deleter>>
{
  using type = std::unique_ptr<Elem, Deleter>::pointer;
};

/**
 * @brief Requires that std::hash<T> overload is provided for the given type. Additionally, it must also return a type
 * that is convertible to std::size_t
 */
template<typename T>
concept Hashable = requires(T a)
{
  {
    std::hash<T>{}(a)
    } -> std::convertible_to<std::size_t>;
};

/**
 * TODO.
 */
template<Hashable Key = std::string, typename map_value = std::any>
class ConstrainedMap
{
  // Type aliases
  using map_type            = typename std::unordered_map<Key, map_value>;
  using iterator            = typename map_type::iterator;
  using mapped_type         = typename map_type::mapped_type;
  using handle_element_type = typename unique_handle_type<map_value>::type;

  // helper function to extract iterator to mapped value.
  map_type hash_table;
  iterator found_value_iter(const std::string& value)
  {
    return hash_table.find(value);
  };
public:
  // Get overload that requires an unique handle
  handle_element_type Get(const std::string& name) requires(unique_handle<map_value>)
  {
    return found_value_iter(name)->second.get();
  };

  // Add overload that requires a unique handle
  handle_element_type Add(const std::string& name, map_value& resource) requires(unique_handle<map_value>)
  {
    hash_table[name] = std::move(resource);
    return hash_table[name].get();
  };

  // Add overload that requires a unique handle and takes an r-value ref
  handle_element_type Add(const std::string& name, map_value&& resource) requires(unique_handle<map_value>)
  {
    hash_table[name] = std::move(resource);
    return hash_table[name].get();
  };

  // Get overload that returns a reference to the value and that the held value is not an unique handle.
  mapped_type& Get(const std::string& name) requires(not(unique_handle<map_value>))
  {
    return found_value_iter(name)->second;
  };

  // Add overload that requires the mapped valuee to not be an unique handle.
  mapped_type& Add(const std::string& name, map_value resource) requires(not(unique_handle<map_value>))
  {
    hash_table[name] = resource;
    return hash_table[name];
  };

  bool Exists(const std::string& name)
  {
    return found_value_iter(name) != hash_table.end();
  }
};
} // namespace fpr