1

I am using the libconfig c++ library to grab stored data, and need to store that data in a string array in c++ without knowing the number of variables that will be passed through a config file. I know that this isn't really possible in c++, but I am trying to find the best practice to do this, and other solutions don't seem to make practical sense for what I am doing. Below is the portion of the code where I am trying to take the string filetype and store all of the results individually in a string array.

    try {
    for (int i = 0; i < cfg.getRoot()["files"].getLength(); ++i) {
        // Only output the record if all of the expected fields are present.
        string filetype;
        if (!(cfg.getRoot()["files"][i].lookupValue("filetype", filetype)))
            continue;

        cout << filetype << endl;
    }
}
catch (const SettingNotFoundException &nfex) {
    // Ignore.
}

Apologies for the probable facepalm you are having right now, I'm a college student still learning the ropes, and am working well ahead of my course at the moment on a personal project.

  • 8
    If you need an array of unknown size you want a `std::vector` – NathanOliver Aug 22 '18 at 20:07
  • Since you don't know the number of elements consider using a container such as std::vector. For configuration it is common to use a container with fast lookup such as std::map or std::unordered_map. – Nir Aug 22 '18 at 20:17
  • I will also add to what everyone else has said already and use a vector. Whenever you do not know the size, always use vector. In most cases you also want to use vector. Also, don’t say, “I know that this isn’t really possible in C++.” C++ is a huge and powerful language and you are just barely scraping the top of what C++ can do. – Sailanarmo Aug 22 '18 at 21:03

1 Answers1

0

I believe your code can be made to behave the way you need with only minimal changes. All you need is a std::vector<std::string> to contain all the fields you record from your loop:

std::vector<std::string> filetypes;

try {
    for (int i = 0; i < cfg.getRoot()["files"].getLength(); ++i) {
        // Only output the record if all of the expected fields are present.
        std::string filetype;
        if (!(cfg.getRoot()["files"][i].lookupValue("filetype", filetype)))
            continue;

        //The use of std::move is optional, it only helps improve performance.
        //Code will be logically correct if you omit it.
        filetypes.emplace_back(std::move(filetype));
    }
}
catch (const SettingNotFoundException &nfex) {
    // Ignore.
}

//Proof that all values have been properly stored.
for(std::string const& filetype : filetypes) {
    std::cout << filetype << std::endl;
}

I don't know what the return type on cfg.getRoot()["files"] is, but it may be worthwhile to store that object to improve the readability of your code:

std::vector<std::string> filetypes;

try {
    //One of these is correct for your code; I don't know which.
    //auto & files = cfg.getRoot()["files"];
    //auto const& files = cfg.getRoot()["files"];
    //I'm assuming this is correct
    auto files = cfg.getRoot()["files"];

    for (auto const& entry : files) {
        // Only output the record if all of the expected fields are present.
        std::string filetype;
        if (!(entry.lookupValue("filetype", filetype)))
            continue;

        //The use of std::move is optional, it only helps improve performance.
        //Code will be logically correct if you omit it.
        filetypes.emplace_back(std::move(filetype));
    }
}
catch (const SettingNotFoundException &nfex) {
    // Ignore.
}

//Proof that all values have been properly stored.
for(std::string const& filetype : filetypes) {
    std::cout << filetype << std::endl;
}
Xirema
  • 19,889
  • 4
  • 32
  • 68