I have a struct with 3 fields, I want to create a map of lambda/function pointers with a key of a hash of the string value of the struct field name. At compile time the map is the checked with an assert: correct length - number of fields in the struct and has no duplicate keys. This works with a map of long as the key and value but not when the value is a lambda. Is there a way to make this work with C++17?
When this is attempted I get error:
error: the type 'const std::pair<long long int, std::function<void(int)> > []' of 'constexpr' variable 'ini' is not literal 107 | constexpr std::pair<long long, std::function<void(int)>> ini[]{
See example here: https://godbolt.org/z/3MjTq94rj Uncomment MakeMapFunction
Header.hpp
struct Container {
int fieldA;
std::string fieldB;
bool fieldC;
};
constexpr inline long long int hash(char const *str, int h = 0)
{
return (!str[h] ? 5381 : (hash(str, h + 1) * 7) ^ str[h]);
}
template <typename T, typename V>
constexpr bool hasDuplicates(const std::pair<T, V> *array, std::size_t size)
{
for (std::size_t i = 1; i < size; i++)
{
for (std::size_t j = 0; j < i; j++)
{
if (array[i].first == array[j].first)
{
return 1;
}
}
}
return 0;
}
// Start of check number of fields in struct
template <typename T, typename... TArgs>
auto isAggregateConstructableImpl(std::tuple<TArgs...>) -> decltype(T{std::declval<TArgs>()...});
template <typename T, typename TArgs, typename = void>
struct isAggregateConstructable : std::false_type
{
};
template <typename T, typename TArgs>
struct isAggregateConstructable<T, TArgs, std::void_t<decltype(isAggregateConstructableImpl<T>(std::declval<TArgs>()))>> : std::true_type
{
};
// Checks if type can be initialized from braced-init-list.
template <typename T, typename TArgs>
constexpr auto isAggregateConstructableV = isAggregateConstructable<T, TArgs>::value;
// Class is convertible to anything.
class any
{
public:
template <typename T>
operator T() const;
};
template <class T, typename... TArgs>
constexpr std::size_t numBindingsImpl()
{
if constexpr (isAggregateConstructable<T, std::tuple<TArgs...>>())
{
return numBindingsImpl<T, any, TArgs...>();
}
else
{
return sizeof...(TArgs) - 1;
}
};
template <typename T>
constexpr auto structGetNumberOfFields = numBindingsImpl<T, any>();
class Holder
{
public:
static std::map<long long, long> MakeMapString();
static const std::map<long long, long> list;
};
source.cpp
std::map<long long, long> Holder::MakeMapString()
{
constexpr std::pair<long long, long> ini[]{
{hash("fieldA"), 55}, // if removed then first static_assert will fail
{hash("fieldB"), 77}, // if changed to fieldA then second static_assert will fail
{hash("fieldC"), 99}};
// This assert determines if the length of ini is the same as number of fields in Container struct
static_assert(end(ini) - begin(ini) == structGetNumberOfFields<Container>, "[Container] has incorrect number of fields");
// This assert checks there are no duplicates in ini
static_assert(!hasDuplicates(ini, std::extent_v<std::remove_reference_t<decltype(ini)>>), "Duplicates Found!");
return {std::begin(ini), std::end(ini)};
}
const std::map<long long, long> listOf = Holder::MakeMapString();