正确放置枚举 class 的散列 table

proper placement of a hash table for enum class

我正在从事一个高级项目,并且有一个关于如何最好地为我的程序实现查找 table 的问题。有几个 enum class 文件包含 enum classes 和一个 operator<< 重载以将它们输出到 std::strings。我们正在使用 boost 属性 树来解析 JSON 文件,默认情况下解析无法将字符串转换为 enum class。由于我们在程序中的某个时刻同时需要枚举 classes 和字符串,因此实现 std::unordered_map 是有意义的。不过,我的问题是根据我的文件将查找 table 放在哪里?

目前我们的代码有 4 个枚举 class 个文件,总结为

namespace wiregen{
    enum class {/* values */}
    ostream& operator<<(ostream& os, enum){/*overload code to output enum as a string*/}
}

枚举需要 public,因为它们被多个 class 使用。我目前已经定义了它们,并且在枚举 header 文件中定义了 operator<< 重载。不过,我的问题是我应该将查找 table 放在枚举 header 中,进行枚举实现并将查找 table 移到那里,还是其他?

虽然这是一个基于设计的问题,但我建议将地图放在匿名命名空间中,以便在界面中隐藏它。这种方法不需要额外的实现文件,因为您可以从花括号列表初始化常量 unordered_map

// SomeHeader.h

enum class Color {                                                      
    Red,                                                                
    Blue,                                                               
    Green,                                                              
    Unknown,                                                            
};                                                                      

// Anonymous namespace to hide our implementation                                                                        
namespace {                                                             
std::unordered_map<std::string, Color> const stringToColorMap_ = {      
    { "Red",   Color::Red },                                            
    { "Blue",  Color::Blue },                                           
    { "Green", Color::Green },                                          
};                                                                      
}                                                                       

// Public interface                                                     
Color colorFromString(std::string const& s) {                           
    auto it = stringToColorMap_.find(s);                                
    if (it != stringToColorMap_.end()) {                                
        return it->second;                                              
    }                                                                   
    return Color::Unknown;                                              
}                                                                       

int main() {                                                            
    cout << static_cast<int>(colorFromString("Red")) << endl;           
    cout << static_cast<int>(colorFromString("Blue")) << endl;          
}                                                                       

作为这个问题的解决方案,我发现最好的解决方案是将枚举包装在 class 中,然后将函数作为 class 的一部分来提供我正在寻找的功能。我不得不将它们从作用域枚举更改为普通枚举,但它现在提供了我们需要的行为。

示例如下

Base.hpp

namespace wiregen{
    class Base{
    public:
        enum baseEnum{
            /*removed for brevity*/
        };

        Base();
        constexpr Base(baseEnum b) : val(b){}
        bool operator==(Base b) const;
        bool operator!=(Base b) const;
        operator std::string() const;
        void operator = (const std::string &str);
    private:
        baseEnum val;
    };

    std::ostream& operator<<(std::ostream& os, const wiregen::Base& b);
}

Base.cpp

namespace{
    typedef boost::bimap<wiregen::Base::baseEnum, std::string>BaseTable;
    typedef BaseTable::value_type baseString;
    BaseTable m_base = boost::assign::list_of<baseString>
        /* removed for brevity */
}

namespace wiregen{
    Base::Base(){val = baseEnum::invalid;}
    bool Base::operator==(Base b) const{return val == b.val;} 
    bool Base::operator!=(Base b) const{return val != b.val;}
    Base::operator std::string() const{
        auto it = m_base.left.find(val);
        if(it != m_base.left.end()){
            return it->second;
        }
        return "invalid";
    }

    void Base::operator = (const std::string &str){
        auto it = m_base.right.find(boost::algorithm::to_lower_copy(str));
        if(it != m_base.right.end()){
            val = it->second;
            return;
        }
        std::cerr<<"Failed to assign Base: "<<str<<std::endl;
        return;
    }

    std::ostream& operator<<(std::ostream& os, const Base& b){
        os << (std::string)b;
        return os;
    }
}