在编译时检查函数映射是否具有重复且结构字段计数大小相同

问题描述 投票:0回答:1

我有一个包含 3 个字段的结构,我想创建一个 lambda/函数指针的映射,其键是结构字段名称的字符串值的哈希值。 在编译时,使用断言检查映射:正确的长度 - 结构中的字段数,并且没有重复的键。 这适用于 long 作为键和值的映射,但不适用于 lambda 值。有没有办法让它在 C++17 上工作?

尝试这样做时,我收到错误:

错误:“constexpr”变量“ini”的类型“const std::pair > []”不是文字 107 | 107 constexpr std::pair> ini[]{

请参阅此处的示例: https://godbolt.org/z/3MjTq94rj 取消注释 MakeMapFunction

标题.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;
};

源码.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();
c++ c++17
1个回答
0
投票

您不能拥有

constexpr std::function
对象。 请记住,
std::function
执行类型擦除和动态分配,并且尚不支持非瞬态分配。 换句话说,
std::function
分配的任何内存都必须在常量表达式结束之前释放。

在您的示例中,您创建的 lambda 都没有捕获,因此您也可以将它们转换为函数指针:

// MakeMapFunction is pointless.
// Let's just use an immediately invoked lambda expression (IILE)

const std::map<long long, void(*)(int)> listOfFunction = [] {
    static constexpr std::pair<long long, void(*)(int)> ini[] {
        {hash("fieldA"), [](int a) {}},
        {hash("fieldB"), [](int a) {}},
        {hash("fieldC"), [](int a) {}}
    };

    // static assertions here ...

    return {std::begin(ini), std::end(ini)};
}();

或者更简洁地说:

const std::map<long long, void(*)(int)> listOfFunction = {
    {hash("fieldA"), [](int a) {}},
    {hash("fieldB"), [](int a) {}},
    {hash("fieldC"), [](int a) {}}
};

// static assertions here ...

顺便说一句,看到

std::map
的哈希值很奇怪。 您也可以使用
std::unordered_map<std::string_view, void(*)(int)>

© www.soinside.com 2019 - 2024. All rights reserved.