Is there an elegant way to represent a map containing different types in C++?

前端 未结 4 1019
小蘑菇
小蘑菇 2021-01-15 13:16

I\'m building a class which I want to configure using various parameters which may one of: int, double and string (or const char

相关标签:
4条回答
  • 2021-01-15 13:48

    The sensible thing to do would be to use Boost.Variant, which is developed by expert C++ programmers and honed and tested by hundreds of projects.

    But if that is not an option for you, I see the following alternatives:

    1. Reimplement Boost.Variant yourself. It will be a wonderful learning excercise, but it will take a lot of time to get right (and then more lots of time to fix all the bugs).

    2. Resign on time efficiency, store all the types in a std::string, and convert them in getters (originally suggested in a comment by @Galik).

    3. Resign on memory efficiency and store all three types in your class.

    4. If you have aversion against Boost specifically, use a different library which provides a variant type (like Qt's QVariant).

    0 讨论(0)
  • 2021-01-15 14:01

    Here is a cut down version of something I regularly use to store program configuration properties, maybe you will find it useful:

    #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';
    }
    
    0 讨论(0)
  • 2021-01-15 14:08

    If I have understood well, you want a std::map<A, B> (or, even better, a std::unordered_map<> since you want hashes) where A is a string and B can be a int, double or std::string.

    For B you can use boost::any<>. Otherwise, if you don't want to use boost, you can use a discriminated union.

    0 讨论(0)
  • 2021-01-15 14:11

    If you want to avoid the use of Boost, you can copy-paste the code of Boost.Variant into your project. It's header-only anyway. Or you can reinvent the wheel by implementing what's commonly called a "discriminated union."

    0 讨论(0)
提交回复
热议问题