C++17 任意名称字典

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

我正在尝试在 C++17 中创建一个字典,它将名称同时关联到任意数量类型的指针,并且能够在查找时推导出类型,而无需在调用方进行显式强制转换。这些类型无法在可变参数模板中预先指定。

所需的界面如下:

AnyDict<std::string, std::map> dict;

// If both inserts succeed this should evaluate to true.
if ( dict.insert(std::string {"Hello"}, 0.5f) &&
     dict.insert(std::string {"Other"}, std::string {"Value"}) )
    ; // ...

// This should return true.
dict.remove(std::string {"Hello"});

// This should return "Other", but if
// something goes wrong returns "".
auto value = dict.find(std::string {"Other"}, std::string {""});

目前我最接近的解决方案包括:

  • 类型到数字的转换
// Currently I'm not using dlls or anything, but...
class TypeIdx
{
public:
    template <class Type>
    static int get()
    {
        // ugly.
        static const int s_idx = s_count++;

        return s_idx;    
    }

private:
    static inline int s_count = 0;
};
  • 字典的包装器,封装字典本身的转换和操作
template <template <class, class> class Dict>
class AnyDict
{
public:
    // constructors, destructor, etc.

    template <class Type>
    bool
    insert(Type& item)
    {
        int key = TypeIdx::get<Type>();
        
        // The delegate must return true on success
        // or false on failure.
        return m_dict.insert(key, (void*) &item);
    }

    template <class Type>
    bool
    remove()
    {
        int key = TypeIdx::get<Type>();
        
        // The delegate must return true on success
        // or false on failure.
        return m_dict.remove(key);
    }

    template <class Type>
    Type*
    find(Type* def)
    {
        int key = TypeIdx::get<Type>();
        
        // The delegate must return a pointer to
        // the item found on success, or to "def"
        // on failure.
        return (Type*) m_dict.find(key, def);
    }

private:
    // Here the void* lets me save a pointer to
    // anything but i can't deduce back the type.
    Dict<int, void*> m_dict;
};

虽然这可以工作并返回正确类型的指针而无需强制转换,但它仍然不会按名称而是按类型对它们进行索引,这不是我想要的。

你知道有什么解决办法吗?也许使用

std::any
会改善这种情况?另外,如果您能为此类建议一个更好的名称,我们将不胜感激。

c++ dictionary templates any
1个回答
0
投票

我认为您应该使用

std::any
作为映射的值类型,因为它完全实现了您需要的类型擦除。无需担心可怕的空指针。这是一个帮助您入门的解决方案:

#include <any>
#include <string>
#include <map>
#include <iostream>


template <typename key_type, template<typename, typename> typename Map>
class AnyDict
{
public:

    AnyDict() = default;

    template <typename value_type>
    bool insert(key_type const& k, value_type const& v) {
        auto [it, rt] = m_values.insert(std::make_pair(k, std::any{v}));
        return rt;
    }

    template <typename value_type>
    value_type find(key_type const& k, value_type const& default_value) const 
    {
        if (auto it = m_values.find(k); it != m_values.end()) {

            if (it->second.type() != typeid(value_type)){
                throw std::logic_error("type mismatch");
            }

            return std::any_cast<value_type>(it->second);
        }
        else {
            return default_value;
        }
    }

    bool remove(key_type const& k) {
        if (auto it = m_values.find(k); it != m_values.end()) {
            m_values.erase(k);
            return true;
        }
        return false;
    }

private:

    Map<key_type, std::any> m_values;
};

int main()
{
    AnyDict<std::string, std::map> dict;

    // If both inserts succeed this should evaluate to true.
    if ( dict.insert(std::string {"Hello"}, 0.5f) &&
        dict.insert(std::string {"Other"}, std::string {"Value"}) )
    {
        std::cout << "Success.\n";
    }

    // This should return true.
    std::cout << std::boolalpha << dict.remove(std::string {"Hello"}) << "\n";

    // This should return "Other", but if
    // something goes wrong returns "".
    auto value = dict.find(std::string {"Other"}, std::string {""});
    std::cout << value << "\n";

};

https://godbolt.org/z/GsrTE7975

现在我认为您想要的界面和您的问题之间存在一些细微的差异。其一,所需的接口不存储任何指针,而是存储值,也就是说,根据接口,

dict
拥有这些值。上面的代码允许您存储任何类型,包括,但不限于指针:https://godbolt.org/z/sfhT68xK9

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