Crash when using natural order for subcommand options
jtojnar opened this issue · 2 comments
The docs say:
Git style subcommands are created by either cli.command("cmd"), which changes the cli objects context to the command, or with opt.command("cmd"), which changes the command that option is for.
However when I try that:
static auto & command = Dim::Cli().command("apple").desc("Change color of the apple.");
static auto & color = command.opt<string>("color", "red");
I get a segfault:
#0 0x00007f2e553ac193 in std::__detail::_List_node_base::_M_hook(std::__detail::_List_node_base*) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#1 0x000055799bf8087d in std::__cxx11::list<std::unique_ptr<Dim::Cli::OptBase, std::default_delete<Dim::Cli::OptBase> >, std::allocator<std::unique_ptr<Dim::Cli::OptBase, std::default_delete<Dim::Cli::OptBase> > > >::_M_insert<std::unique_ptr<Dim::Cli::OptBase, std::default_delete<Dim::Cli::OptBase> > > (this=0x7ffcab754a50, __position=...) at /usr/include/c++/10/bits/stl_list.h:1912
#2 0x000055799bf7dcff in std::__cxx11::list<std::unique_ptr<Dim::Cli::OptBase, std::default_delete<Dim::Cli::OptBase> >, std::allocator<std::unique_ptr<Dim::Cli::OptBase, std::default_delete<Dim::Cli::OptBase> > > >::push_back (this=0x7ffcab754a50, __x=...) at /usr/include/c++/10/bits/stl_list.h:1217
#3 0x000055799bf562cf in Dim::Cli::addOpt (this=0x7ffcab754990, src=...) at _deps/dimcli-src/libs/dimcli/cli.cpp:1273
#4 0x000055799bf48caa in Dim::Cli::addOpt<Dim::Cli::Opt<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > (this=0x7ffcab754990, ptr=...)
at _deps/dimcli-src/libs/dimcli/cli.h:885
#5 0x000055799bf47df3 in Dim::Cli::opt<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, void, void> (this=0x7ffcab754990, value=0x0, names=..., def=...) at _deps/dimcli-src/libs/dimcli/cli.h:811
#6 0x000055799bf47747 in Dim::Cli::opt<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > (this=0x7ffcab754990, names=..., def=...) at _deps/dimcli-src/libs/dimcli/cli.h:856
#7 0x000055799bf46b69 in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535) at ../main.cpp:9
#8 0x000055799bf46d0e in _GLOBAL__sub_I__Z5appleRN3Dim3CliE () at ../main.cpp:26
#9 0x000055799bf8e45d in __libc_csu_init ()
#10 0x00007f2e54fc04f1 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
#11 0x000055799bf462ee in _start ()
Reproducible example: https://github.com/jtojnar/repro/tree/f742c8ef00ae5f9677a65ebce29201cfa52918c7/dimcli-natural-static
Thanks for the report!
Unlike Opt<>'s, capturing a Cli object by reference doesn't give you something you can use, instead you could do:
static auto command = Dim::Cli().command("apple").desc("Change color of the apple.");
static auto & color = command.opt<string>("color", "red");
or:
static Dim::Cli s_cli;
static auto & command = s_cli.command("apple").desc("Change color of the apple.");
static auto & color = command.opt<string>("color", "red");
or even:
static auto & color = Dim::Cli().command("apple")
.desc("Change color of the apple.")
.opt<string>("color", "red");
I agree that this is surprising and hard to diagnose. It should be improved so that capturing a Cli() by reference either:
- works
- asserts about what's wrong
- or at least, has a hard to missing warning in the docs and code comments.
It currently behaves this way because when you construct a Cli() you are making a handle to the global configuration, and when it goes out of scope it no longer points at anything. Where as what is returned by cli.opt() is a reference to an object that continues to live in the global configuration.
I will investigate further.
I pushed a fix, going with number 4, if you try you get a compile error.
auto & c1 = Dim::Cli(); // compile error, it's illegal to bind a temporary to a non-const reference
// BEFORE
// command(...) returned a self referencing Cli& hiding that it's a temporary, leading to a crash
auto & c2 = Dim::Cli().command("cmd");
// NOW
// command(...) and similar methods called on a temporary return Cli&& propagating it's status as a
// temporary. And trying to bind it to a non-const reference gets you the same compile error as for
// c1 above.
auto & c2 = Dim::Cli().command("cmd");
Will add coverage tests and figure out what to do with the docs in a couple of days.