有一种优雅的方式来表示包含C ++中不同类型的地图吗?

时间:2014-09-23 06:28:43

标签: c++ types

我正在构建一个我想要使用各种参数进行配置的类,其中包括以下参数之一:intdoublestring(或const char *物)。在像Ruby这样的语言中,我将构建一个初始化函数,该函数接受由字符串键入的哈希值。例如:

class Example
  def init_with_params params
    puts params
  end
end

e = Example.new
e.init_with_params({ "OutputFile" => "/tmp/out.txt", "CaptureFPS" => 25.0, "RetryDelaySeconds" => 5 })

如何在C ++中创建类似的行为?

环顾四周,我发现有几个帖子在讨论boost::variant。我宁愿避免使用boost如果通过将不同类型限制为上面提到的3种类型,可以制作一个相当干净的解决方案。

修改: 我同意使用经过精心设计和广泛测试的代码(如Boost.Variant)比重新实现相同的想法要好得多。在这种情况下,问题减少到只使用3种基本类型,所以我一直在寻找最简单的实现方法。

4 个答案:

答案 0 :(得分:6)

如果您想避免使用Boost,可以将Boost.Variant的代码复制粘贴到项目中。无论如何它只是标题。或者你可以通过实施通常所谓的“歧视联盟”重新发明轮子。

答案 1 :(得分:5)

这是我经常用来存储程序配置属性的缩减版本,也许你会发现它很有用:

#include <map>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <initializer_list>

class config
{
    // everything is stored internally as std::strings
    // usually read from a configuration file
    typedef std::map<std::string, std::string> prop_map;
    typedef prop_map::const_iterator prop_map_citer;

    prop_map props;

public:
    // example constructor. Normally I have a method to read the
    // values in from a file
    config(std::initializer_list<std::pair<std::string, std::string>> list)
    {
        for(const auto& p: list)
            props[p.first] = p.second;
    }

    // values are converted as they are requested to whatever types
    // they need to be. Also a default may be given in case the value
    // was missing from the configuration file
    template<typename T>
    T get(const std::string& s, const T& dflt = T()) const
    {
        prop_map_citer found = props.find(s);

        if(found == props.end())
            return dflt;

        T t;
        std::istringstream(found->second) >> std::boolalpha >> t;
        return t;
    }

    // std::strings need special handling (no conversion)
    std::string get(const std::string& s, const std::string& dflt = "") const
    {
        prop_map_citer found = props.find(s);
        return found != props.end() ? found->second : dflt;
    }

};

int main()
{
    const config cfg =
    {
        {"OutputFile", "/tmp/out.txt"}
        , {"CaptureFPS", "25.0"}
        , {"RetryDelaySeconds", "5"}
    };

    std::string s;
    float f;
    int i;

    s = cfg.get("OutputFile");
    f = cfg.get<float>("CaptureFPS");
    i = cfg.get<int>("RetryDelaySeconds");

    std::cout << "s: " << s << '\n';
    std::cout << "f: " << f << '\n';
    std::cout << "i: " << i << '\n';
}

答案 2 :(得分:3)

明智的做法是使用Boost.Variant,它由专业的C ++程序员开发,经过数百个项目的磨练和测试。

但如果这不是您的选择,我会看到以下替代方案:

  1. 重新实现Boost.Variant自己。这将是一个很好的学习练习,但它需要很多的时间才能正确(然后有更多的时间来修复所有的错误)。

  2. 降低时间效率,将所有类型存储在std::string中,并将其转换为getter(最初在@Galik的评论中提出)。

  3. 降低内存效率并将所有三种类型存储在您的班级中。

  4. 如果您特别厌恶Boost,请使用提供变体类型的其他库(例如Qt's QVariant)。

答案 3 :(得分:2)

如果我理解得很好,你需要一个std::map<A, B>(或更好的,std::unordered_map<>,因为你想要哈希)A是一个字符串而B可以是intdoublestd::string

对于B,您可以使用boost::any<>。否则,如果您不想使用提升,则可以使用受歧视的union