I am trying to create a static map for where the type of the key is an enum, and the values and keys are stored inside an std::array
.
The below code works nicely for trivially copiable types, but unfortunately fails for types such as std::mutex
and std::atomic<T>
.
#pragma once
#include <magic_enum.hpp>
#include <bitset>
#include <array>
template <typename Key, typename T>
struct EnumMap {
static_assert(std::is_enum<Key>::value);
using key_type = Key;
using mapped_type = T;
using value_type = std::pair<const key_type, mapped_type>;
using size_type = std::size_t;
EnumMap() : data_(initializeData()){
}
private:
static constexpr size_type enum_size_ = magic_enum::enum_count<Key>();
std::array<value_type, enum_size_> data_;
std::bitset<enum_size_> filled_;
constexpr auto initializeData() {
return initializeDataHelper(std::make_index_sequence<enum_size_>());
}
template <size_type... Is>
constexpr auto initializeDataHelper(std::index_sequence<Is...>) {
constexpr auto enum_entries = magic_enum::enum_values<Key>();
return std::array<value_type, enum_size_>{{std::make_pair(enum_entries[Is], T{})... }};
}
};
Can anyone think of a version where std::mutex
would work?
I am guessing placement new could be used for this, but perhaps there is a nicer index_sequence
based solution also.
For the uninitiated to the black magic performed by @Passer By, std::pair
also offers a special constructor using tuples, allowing you to generate a potentially empty parameter pack:
template< class... Args1, class... Args2 >
pair( std::piecewise_construct_t,
std::tuple<Args1...> first_args,
std::tuple<Args2...> second_args );
This allows you to explicitly pass zero parameters, i.e.
return std::array<value_type, enum_size_>{ value_type(std::piecewise_construct, std::make_tuple(enum_entries[Is]), std::make_tuple())...};
This is also extendable to the non-default constructible types:
template<typename... Args>
EnumMap(Args&&... args) : data_(initializeData(std::make_tuple(std::forward<Args>(args)...))) {}
template<typename Init>
constexpr auto initializeData(Init&& init) {
return initializeDataHelper(
std::make_index_sequence<enum_size_>(),
std::forward<Init>(init)
);
}
template <size_type... Is, typename Init>
constexpr auto initializeDataHelper(std::index_sequence<Is...>, Init&& init) {
constexpr auto enum_entries = magic_enum::enum_values<Key>();
return std::array<value_type, enum_size_>{
value_type(std::piecewise_construct, std::make_tuple(enum_entries[Is]), std::forward<Init>(init))...
};
}
Same example: https://godbolt.org/z/dEEWqo3v1