Returning a map of string and different vector dat

2019-07-27 01:52发布

问题:

I know questions like this has been asked before, but I am still struggling to find an answer.

I am trying to return a method of map of string and vector; I have two vectors std::vector<Eigen::MatrixXd> samples and std::vector<std::string> sample_names

This is what I have done so far:

std::map<std::string, std::vector> FileReader::get_data() {
    std::map<std::string, std::vector> content;

    std::vector<Eigen::MatrixXd> samples;
    std::vector<std::string> sample_names;

    for (auto i : list_dir()) {
        if (contains_number(i)) {
            samples.push_back(load_csv(location + "/" + i));
            sample_names.push_back(i);
        }
    }

    content["samples"] = samples;
    content["sample_names"] = sample_names;

    return content;
}

Obviously, I am getting an error of

error: type/value mismatch at argument 2 in template parameter list for 'template<class _Key, class _Tp, class _Compare, class _Alloc> class std::map'
         std::map<std::string, std::vector> get_data(void);

Because I don't have a datatype for vector in the method. If I add string or Eigen::MatrixXd they both cannot go hand-in-hand. I know that I could use boost::any but it would be an extra library which needs to be compiled (?), and C++ is not dynamically typed.

Is there any way I could do this by not have an extra library?

Update-1:

Another way I think I could do is that in the constructor have a private variable of vector<Eigen::MatriXd> samples & vector<std::string> smaple_names and create two methods of same vector types get_samples() and get_sample_names() then use something like this->samples = samples and this->sample_name. Is this the only other way?

Update-2:

It looks like std::any is in C++17 - http://en.cppreference.com/w/cpp/header/any. If anyone :p interested.

Update-3:

If anyone is looking for the answer it's at

https://stackoverflow.com/a/44896365/3782963

回答1:

I agree that std::any (or boost::any) is a bit overkill for this situation. std::variant (or boost::variant) would be a better choice here, because you know exactly what 2 types you want to store.

If you don't want to use any additional libraries like Boost and don't have access to C++17, you can try to emulate std::variant yourself in a dummy implementation (if you can't/don't want to write a full one). Something like:

struct data {
    // constructors...

    std::vector<Eigen::MatrixXd> samples;
    std::vector<std::string> names;
    int index = -1;
};

Then you can store your data in a std::map<std::string, data>:

std::map<std::string, data> content;
content.emplace(std::make_pair("samples", data{samples, 0}));
content.emplace(std::make_pair("sample_names", data{sample_names, 1}));

Then you just need to check the index to know which std::vector you have to use.



回答2:

Why use a map at all? You could use std::pair<std::vector<std::string>, std::vector<Eigen::MatrixXd>> instead.

Here is what the function would look like:

std::pair<std::vector<std::string>, std::vector<Eigen::MatrixXd>> FileReader::get_data() 
{
    std::vector<Eigen::MatrixXd> samples;
    std::vector<std::string> sample_names;

    for (auto i : list_dir()) {
        if (contains_number(i)) {
            samples.push_back(load_csv(location + "/" + i));
            sample_names.push_back(i);
        }
    }

    return std::make_pair(sample_names, samples);
}

If you're using C++11, you can replace the return statement with just

return {sample_names, samples};


标签: c++ vector