c++compile-timeflatmapunordered

Implementation of Static Flat Map


How to implement a flat map using arrays when keys are known at compile time. Imagine I know my keys:

enum class Asset
{
    FONT,
    TEXTURE
};

struct AssetContainer
{
};

Hence I could do:

struct STATIC_DATA
{
   inline static std::unordered_map<Asset, std::atomic<std::shared_ptr<const AssetContainer>>> ASSET_CACHE = { 
      {Asset::FONT,    nullptr},
      {Asset::TEXTURE, nullptr}
   };

it looks like since I know my keys and size I can optimize this by writing a const size map using arrays as:

template<class Key, class Value, size_t Size>
struct static_map
{
    using container = std::array<std::pair<Key, Value>, Size>;
}
  1. Will this add any gain in terms of speed accessing values knowing that the max size < 50?
  2. Is this implemented somewhere in the standard?
  3. If not 2 how can I implement this in a simple and optimized way? Should I have keys and values as arrays, or 2 arrays containers? I think first one is cache friendly but do I gain anything for size < 50.
  4. How can I implement initializer lists constructor without supplying size so I can do:
struct STATIC_DATA
{
   inline static static_map<Asset, std::atomic<std::shared_ptr<const AssetContainer>>> ASSET_CACHE = { 
      {Asset::FONT,    nullptr},
      {Asset::TEXTURE, nullptr}
   };
};

Solution

  • The compiler will not optimize an unordered map into an array.

    template<class Enum, class Value, std::size_t Count>
    struct EnumMap:std::array<Value,Count> {
      // Get the array out (base class):
      constexpr std::array<Value, Count>& Array() { return *this; }
      constexpr std::array<Value, Count> const& Array() const { return *this; }
    
      // lookup in base (array):
      constexpr Value& operator[]( Enum e ) {
        return Array()[ static_cast<std::underlying_type_t<Enum>>(e) ];
      }
      // use non-const version and cast back to const:
      constexpr Value const& operator[]( Enum e ) const {
        return const_cast<Value const&>( const_cast<EnumMap&>(*this)[e] );
      }
    
      // Build from key-value pairs:
      constexpr EnumMap( std::initializer_list<std::pair<Enum const, Value const>> il ) {
        for (auto const&(key, value):il)
          (*this)[key] = value;
      }
    };
    

    that should do it reasonably well. It does needlessly default construct the values.

    EnumMap<Asset, std::atomic<std::shared_ptr<const AssetContainer>>, 2> foo = {
      {Asset::FONT,    nullptr},
      {Asset::TEXTURE, nullptr}
    };