26

I am writing a program that uses Boost's Program Options library and I noticed the following syntax that has haunted me since I saw it:

desc.add_options()
        ("help","produce help message")
        ( /* other flag, value, description pairs here */)
;

I see that in the header, operator() is overridden, but I'm not sure how that allows this to be syntactically correct.

Secondly, is there any advantage to this syntax, compared with just calling add_options() multiple times (besides showing off the fact that you can manipulate syntax like this)?

paulrehkugler
  • 3,241
  • 24
  • 45

2 Answers2

24

The add_options member function returns an object of type options_description_easy_init. The latter has operator() overloaded to return a reference to itself. This allows you to chain the calls as you've shown in the snippet.

The difference between chaining the calls and calling add_options several times is that in the former case a single instance of options_description_easy_init is created and each time you invoke operator() on it, it adds the options to the owner (options_description). If you were to call add_options multiple times each call would create a new instance of options_description_easy_init.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
14

The advantage question is subjective, but in this case it's brevity.

Compare this from one of my home projects:

("help,h", "Generate this help message")
("output-file,o", po::value<std::string>(), "Output filename. Required.")
("tangent,t", "Generate/load tangent-space basis.")
("collada-output,c", "Write a Collada file, rather than our mesh XML format.")
("arrays,a", "Write arrays instead of indexed verts. Cannot combine with Collada writing.")
("flip-tangent,f", "Change the tangent-space basis matrix's handedness. Negates bitangent.")
("map", po::value<std::string>(), "Map filename. Defaults to the ColladaConv directory's 'stdmap.txt' file.")
("vao", po::value<std::vector<std::string> >(), "Sequence of mappings, of the form:\n"
        "Name # # # #\n"
        "\n"
        "Each # is an attribute index to use for this VAO.\n"
        "Each VAO name must be unique; you cannot use the same VAO in the same place.")

to this:

visible.add_options()("help,h", "Generate this help message")
visible.add_options()("output-file,o", po::value<std::string>(), "Output filename. Required.")
visible.add_options()("tangent,t", "Generate/load tangent-space basis.");
visible.add_options()("collada-output,c", "Write a Collada file, rather than our mesh XML format.");
visible.add_options()("arrays,a", "Write arrays instead of indexed verts. Cannot combine with Collada writing.");
visible.add_options()("flip-tangent,f", "Change the tangent-space basis matrix's handedness. Negates bitangent.");
visible.add_options()("map", po::value<std::string>(), "Map filename. Defaults to the ColladaConv directory's 'stdmap.txt' file.");
visible.add_options()("vao", po::value<std::vector<std::string> >(), "Sequence of mappings, of the form:\n"
        "Name # # # #\n"
        "\n"
        "Each # is an attribute index to use for this VAO.\n"
        "Each VAO name must be unique; you cannot use the same VAO in the same place.");

Line length matters. And not having to have visible.add_options() in front of everything makes it easier to read.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    I suspected that was the only advantage. In my opinion, that's a good bit more work to just make prettier code. – paulrehkugler May 07 '12 at 20:08
  • Both examples are pretty hideous. Could you define `options_description_easy_init o = visible.add_options()` and simply call `o("help", "Generate this help message");`? – Vortico May 11 '12 at 21:56
  • @Vortico: I don't see anything especially unpleasant about the first case. Indeed, I don't see what your suggestion has as a benefit over the first one, even if it does work. I don't know if it will; you'd have to check Boost's documentation to see. But generally, it's not a good idea to keep intermediates from constructs like this. – Nicol Bolas May 11 '12 at 22:27
  • Not rather `v.ao(...);`? – Aconcagua Jan 27 '19 at 06:02
  • 1
    @Aconcagua: What are `v` and `ao`? At least with the current syntax, it's clear that each set of parens is doing something related to `add_options`. With yours, it's about symbols with arcane names. – Nicol Bolas Jan 27 '19 at 06:06
  • @NicolBolas I just abreviated the names, thought it would be obvious... Second example is lacking the semicolons, and I'd assume *if* library did it this way, there wouldn't be any intermediate callable objects any more... – Aconcagua Jan 27 '19 at 06:48
  • I prefer the non-chained option, simply because my code formatter tries putting everything on one line with the second. – Roddy May 11 '22 at 08:15