1

I am trying to parse a neural network configuration file similar to the following lines. Actual file will have many more lines but similar format.

Resnet50 {
    Layer CONV1 {
        Type: CONV
        Stride { X: 2, Y: 2 }       
        Dimensions { K: 64, C: 3, R: 7, S: 7, Y:224, X:224 }
    }


    Layer CONV2_1_1 {
        Type: CONV
        Stride { X: 1, Y: 1 }       
        Dimensions { K: 64, C: 64, R: 1, S: 1, Y: 56, X: 56 }
    }

I use this Boost argument parsing code:

void to_cout(const std::vector<std::string> &v)
{
   std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>{std::cout, "\n"});
}

int main(int argc, char* argv[]) {
   namespace po = boost::program_options;
   po::options_description conf("Config file options");
   conf.add_options()("confg_file", po::value<std::string>(&file_name), "HW configuration file");
   po::options_description all_options;
   all_options.add(conf);
   po::variables_map vm;
   po::store(po::parse_command_line(argc, argv, all_options), vm);
   po::notify(vm);

   return 0;
}

Seeming a regular parsing routine. But the configuration file wasn't parsed correctly because there was no output in the to_cout of vm. How does parse_command_line get into the hierarchy of the example configuration file?

sehe
  • 374,641
  • 47
  • 450
  • 633
visitor99999
  • 163
  • 1
  • 10

1 Answers1

0

That's not what Program Options is about. You can use it to read ini files, but not with the code shown. You are literally invoking parse_command_line (not parse_config_file).

The code you show allows you to parse the name of a config file from the command line. This is also why the value is std::string file_name.

Maybe we're missing (quite a lot of) code, because there's also nothing invoking to_cout in your code, nevermind that it wouldn't work with vm because the argument type doesn't directly match. I know you can loop over matched names in the variable map, and this is likely what you did, but that's all not very relevant.

Even if you did call parse_config_file would not know how to read that file format, as the documented format is an INI-file flavour.

The Good News

The good news is that your config file does have a format that closely resembles INFO files as supported by Boost Property Tree. Which gives me the first opportunity in 10 years¹ to actually suggest using that library: It seems to be more or less precisely what you are after:

Live On Coliru

#include <boost/property_tree/info_parser.hpp>
#include <iostream>

extern std::string config;

int main() {
    boost::property_tree::ptree pt;
    std::istringstream iss(config);
    read_info(iss, pt);

    write_info(std::cout, pt);
}

std::string config = R"(
Resnet50 {
    Layer CONV1 {
        Type: CONV
        Stride { X: 2, Y: 2 }       
        Dimensions { K: 64, C: 3, R: 7, S: 7, Y:224, X:224 }
    }


    Layer CONV2_1_1 {
        Type: CONV
        Stride { X: 1, Y: 1 }       
        Dimensions { K: 64, C: 64, R: 1, S: 1, Y: 56, X: 56 }
    }
}
)";

Prints

Resnet50
{
    Layer CONV1
    {
        Type: CONV
        Stride
        {
            X: 2,
            Y: 2
        }
        Dimensions
        {
            K: 64,
            C: 3,
            R: 7,
            S: 7,
            Y:224, X:224
        }
    }
    Layer CONV2_1_1
    {
        Type: CONV
        Stride
        {
            X: 1,
            Y: 1
        }
        Dimensions
        {
            K: 64,
            C: 64,
            R: 1,
            S: 1,
            Y: 56,
            X: 56
        }
    }
}

Tieing It Together

You may tie it together with a CLI argument for the filename:

Live On Coliru

#include <boost/property_tree/info_parser.hpp>
#include <boost/program_options.hpp>
#include <iostream>
using boost::property_tree::ptree;

int main(int argc, char* argv[]) {
    std::string file_name;
    {
        namespace po = boost::program_options;
        po::options_description cliopts("options");
        cliopts.add_options() //
            ("config_file", po::value<std::string>(&file_name),
             "HW configuration file");

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, cliopts), vm);

        if (!vm.contains("config_file")) {
            std::cerr << cliopts << '\n';
            return 255;
        }

        po::notify(vm); // sets file_name
    }

    boost::property_tree::ptree pt;
    {
        std::ifstream ifs(file_name);
        read_info(ifs, pt);
    } // closes file

    for (auto const& [key, sub] : pt.get_child("Resnet50")) {
        std::cout << key << " " << sub.get_value("") << "\n";
    }
}

Then for running ./test.exe --config_file config.cfg it may print e.g.

Layer CONV1
Layer CONV2_1_1

¹ 10 years (and more) of admonishing people not to abuse Property Tree as an XML, INI, or JSON parser, because it is none of these things. It's ... a property tree library.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks. Let me try. I didn't use to_cout because there is nothing to print after execution. In terms of why I used parse_command_line instead of parse_config_file, this Boost document says they are the same: "boost::program_options::parse_config_file() does the same thing as boost::program_options::parse_command_line() and returns parsed options in an object of type boost::program_options::parsed_options. This object is passed to boost::program_options::store() to store the parsed options in vm." https://theboostcpplibraries.com/boost.program_options. – visitor99999 May 26 '21 at 20:37
  • by the way, can pt.get_child() go deeper than the first Layer of the hierarchy? Those X, Y, K, etc need to be parsed too. – visitor99999 May 26 '21 at 20:39
  • I had to change some lines in your code to get pt.get_child() working, like define a "ptree children = pt.get_child("Resnet50")". And I had to comment out vm.contains() because there is no contains() method in vm. Code can compile without error but still can't get expected output – visitor99999 May 26 '21 at 21:08
  • I think I need to write another parser to decode the configuration file. Boost Options can only register the file name. It's not touching anything insider the file. The parse will just go through the config file line-by-line and parse each token in each line. – visitor99999 May 26 '21 at 23:08
  • Okay. Where do I begin. You rushing to the wrong conclusions. 1. _"`parse_command_line` vs `parse_config_file`"_: the site linked is not a boost document (they're at https://boost.org). And also it clearly doesn't say the functions are the same. It does (correctly) explain they perform a similar task. How can it do the same thing, if they take very different function parameters? – sehe May 27 '21 at 10:30
  • 2. _"can `pt.get_child()` go deeper"_? Sure, documentation is right there: https://www.boost.org/doc/libs/1_76_0/doc/html/property_tree/synopsis.html, also for `put`/`put_child`. – sehe May 27 '21 at 10:30
  • 3. _"Those X, Y, K, etc need to be parsed too."_ Did you miss my answer? How? It [prints](https://stackoverflow.com/questions/67709260/boost-option-to-parse-a-configuration-file/67711853?noredirect=1#:~:text=56-,Prints,Resnet50,-Layer) the entire hierarchy after parsing. It's even [Live On Coliru](http://coliru.stacked-crooked.com/a/ec055bbd031fa9ba) – sehe May 27 '21 at 10:31
  • 4. "_I had to comment out vm.contains()"_ - just replace it with `vm.count()`. `contains` was [added in c++20](https://en.cppreference.com/w/cpp/container/map/contains). – sehe May 27 '21 at 10:31
  • 5. _"like define a "ptree children = pt.get_child("Resnet50")"_ - that one is weird. Compare: [c++17](http://coliru.stacked-crooked.com/a/545befa837fd4095) [c++11/14](http://coliru.stacked-crooked.com/a/54c347732d46b165), [c++03](http://coliru.stacked-crooked.com/a/09e3aa1bc14b3114) (all live demos). Regardless, you can save yourself trouble by tagging your question appropriately (or, at all, I think I've even added [tag:c++] for you) – sehe May 27 '21 at 10:31
  • 6. "_I need to write another parser"_ Yes but no. Yes: Boost Program Options cannot do it. No: you likely don't have to write it yourself, as Boost Property Tree seems to handle it? I don't see your entire file, but you can see that it handles the sample shown just fine. Again, when in doubt, see the documentation: https://www.boost.org/doc/libs/1_76_0/doc/html/property_tree/parsers.html#property_tree.parsers.info_parser – sehe May 27 '21 at 10:31
  • 7, "_Boost Options can only register the file name."_ That was the gist of my answer (before the ["The Good News"](https://stackoverflow.com/questions/67709260/boost-option-to-parse-a-configuration-file/67711853?noredirect=1#:~:text=The%20Good%20News,good%20news%20is) heading) – sehe May 27 '21 at 10:32
  • 8. "_It's not touching anything inside the file."_ That's just not true. `parse_config_file` will read the file alright, but your file is not in the correct format for it. – sehe May 27 '21 at 10:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/232943/discussion-between-sehe-and-visitor99999). – sehe May 27 '21 at 10:32
  • whew, so many replies :-). I couldn't run your code as it is on c++14 in VS2019. You might have used c++20 on Coliru. I did look into Boost Property Tree a bit yesterday, I felt it can't handle variance in the configuration files (with different network configurations). But I am very likely wrong 'cause I am not sure how this works yet. Let me take a look at your document link. – visitor99999 May 27 '21 at 16:04
  • 1
    Ok, I studied Property Tree examples and will open a separate post on that. Please chime in there or in chat (sorry missed you there!) – visitor99999 May 27 '21 at 21:09