Question

I've a config file format I was hoping to implement with Boost Program Options (as I've used that library before), but I somehow have to implement blocks like this:

label = whatever
depth = 3

start
source = /etc
dest = /tmp/etc/
end

start
source = /usr/local/include
dest = /tmp/include
depth = 1
end

I read in the docs that I can have [sections], so I first wondered about this:

label = whatever
depth = 3

[dir]
source = /etc
dest = /tmp/etc/

[dir]
source = /usr/local/include
dest = /tmp/include
depth = 1

But if I've understood correctly, the dir becomes part of the variable name, so duplicates would not be possible, and this would not work. So then I wondered about moving source to be the section name:

label = whatever
depth = 3

[/etc]
dest = /tmp/etc/

[/usr/local/include]
dest = /tmp/include
depth = 1

Does that seem a reasonable approach? I'm wondering how I iterate through a list of sections, when I don't know the section names in advance?

Or, is there a better way to use the Program Options library to achieve this?

Était-ce utile?

La solution

Perhaps you should use Boost property_tree instead of program_options, as your file format appears to be very similar to the Windows INI file format. Boost property_tree has a parser for INI files (and a serializer in case you need that, too).

Processing your options would then be implemented by traversing the tree. Options not in a section will be under the tree root, and section options will be under a node for that section.

You can use program_options if you really want to. The key is to pass true for the final argument to parse_config_file, which is allow_unregistered_options:

#include <iostream>
#include <sstream>
#include <boost/program_options.hpp>

static const std::string fileData = // sample input
   "foo=1\n"
   "[bar]\n"
   "foo=a distinct foo\n"
   "[/etc]\n"
   "baz=all\n"
   "baz=multiple\n"
   "baz=values\n"
   "a.baz=appear\n";

int main(int argc, char *argv[]) {
   namespace po = boost::program_options;

   std::istringstream is(fileData);
   po::parsed_options parsedOptions = po::parse_config_file(
      is,
      po::options_description(),
      true);                     // <== allow unregistered options

   // Print out results.
   for (const auto& option : parsedOptions.options) {
      std::cout << option.string_key << ':';

      // Option value is a vector of strings.
      for (const auto& value : option.value)
         std::cout << ' ' << value;
      std::cout << '\n';
   }

   return 0;
}

This outputs:

$ ./po
foo: 1
bar.foo: a distinct foo
/etc.baz: all
/etc.baz: multiple
/etc.baz: values
/etc.baz: appear

However, note that what you get with this approach is a vector of options and not the map that the typical use of program_options produces. So you may end up processing the parsed_options container into something you can query more easily, and that something may look like a property_tree.

Here's a similar program that uses property_tree. The input is slightly different because property_tree does not allow duplicate keys.

#include <iostream>
#include <sstream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>

static const std::string fileData = // sample input                             
   "foo=1\n"
   "[bar]\n"
   "foo=a distinct foo\n"
   "[/etc]\n"
   "foo=and another\n"
   "baz=all\n";

static void print_recursive(
   const std::string& prefix,
   const boost::property_tree::ptree& ptree) {
   for (const auto& entry : ptree) {
      const std::string& key = entry.first;
      const boost::property_tree::ptree& value = entry.second;
      if (!value.data().empty())
         std::cout << prefix + key << ": " << value.data() << '\n';
      else
         print_recursive(prefix + key + '.', value);
   }
}

int main() {
   namespace pt = boost::property_tree;

   std::istringstream is(fileData);
   pt::ptree root;
   pt::read_ini(is, root);

   print_recursive("", root);
   return 0;
}

Autres conseils

Don't know about the Boost Programs Options library. But the INI file format has a built-in support in the Win32 API.

In order to read all sections in the INI file without knowing their names you can use MSDN Win32 GetPrivateProfileSectionNames

In order to read all keys in a section you can use MSDN Win32 GetPrivateProfileSection

This API is said to be only for compatibility and in the background it can map INI file keys to the registry.

In practice it should work fine (it always did). You can even have UNICODE INI files assuming they have the BOM mark and you call the ..W versions of the API.

Suitability of the config file format and it's location (e.g. if it can be stored in registry) depends on your deployment scenarios

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top