How to create option aliases with boost::program_options? - c++

I would like to be able to have the possibility to create option aliases with boost::program_options that stores their arguments under the same key/label.
The architecture of my software uses different specialized option parsers depending on the value argv[1]. However some options are shared, like my option --inputs.
inputOptions.add_options()
("--inputs",
po::value< std::vector<std::string> >()->value_name("paths"),
"List of files to edit.\n");
For compatibility with older version of the program, I would like to add to one of the sub-parsers a compatibility option --input that stores its argument(s) under "--inputs". Ideally that option should take at most one argument instead of arbitrarily many. However if you provide a solution that makes --input identical to --inputs, I guess it's fine too, as in this case positional options are sent to "--inputs" anyway.
Thank you for any help !

You can use extra_parser(see Non-conventional syntax in the docs), i.e. parser that can be used to manipulates tokens from input before they are processed further. It can be used for things like translating --run into --command=run etc.
Extra parser is a functor that has the following signature:
std::pair<std::string, std::string>(const std::string &s)
It should return option name in pair::first and (optional) option value in pair::second. Empty pair::first means that the extra parser has not parsed anything. Value (i.e. pair::second) can be empty - only option name has been parsed. If returned pair is valid then the name or name/value pair is used instead of parsing the original token via normal machinery.
First, we write aliasing function:
using OptionAliases = std::map<std::string, std::string>;
std::pair<std::string, std::string>
renameOptions(const std::string &token, const OptionAliases &aliases)
{
auto rtoken(boost::make_iterator_range(token));
// consume "--" prefix
if (!boost::algorithm::starts_with(rtoken, "--")) { return { "", "" }; }
rtoken.advance_begin(2);
// find equal sign (returns iterator range)
const auto eq(boost::algorithm::find_first(rtoken, "="));
// extract option (between "--prefix" and "="/end()) and map it to output
const auto faliases(aliases.find(std::string(rtoken.begin(), eq.begin())));
if (faliases == aliases.end()) { return { "", "" }; }
// return remapped option and (optionally) value after "="
return std::make_pair(faliases->second
, std::string(eq.end(), rtoken.end()));
}
It simply splits input token into --, name, =, value (no value if there is no = sign) and if the name is found in the provided alias mapping it returns (remapped-name, value).
Then, we create the parser itself, using lambda:
boost::program_options::ext_parser optionAlias(OptionAliases &&aliases)
{
return [aliases{std::move(aliases)}](const std::string &token)
{
return renameOptions(token, aliases);
};
}
(Needs at least C++14, for C++11 change to return [aliases](con...)
You can plug this parser into cmdline parser:
parser.extra_parser(optionAlias({{"mark.twain", "samuel.clemens"}
, {"lewis.caroll", "charles.dodgson"}}));
Now, in the example above, both --mark.twain and --samuel.clemens will point to vars["samuel.clemens"] and both --lewis.caroll and --charles.dodgson will point to vars["charles.dodgson"].
Caveats:
Works only for command line parser.
Expects allow_long style (long options with -- prefix). Can be changed in the code.
Expects long_allow_adjacent style (values allowed in one token using =). Can be changed in the code, as well.
If there is any non-option token that parses as --alias then it is translated as well since there is no context. No way to circumvent.
Example: If there's an option with name name that expects value and long_allow_next style is used, then --name=--mark.twain will be parsed as option name with value --mark.twain (as expected) while --name --mark.twain will be parsed as option name with value samuel.clemens.
Short of that, it works as expected.
Hope it helps.

Have you tried using the form po::value<...>(&variable)? The value of the option is saved directly to the variable after parsed. You can then add two options --input and --inputs pointing to the same variable. Additionally, you'd probably have to check that only one of the two options is used and, otherwise, show an error message.
I hope I've understood your question correctly.

By default boost::program_options allows the prefix of a long option to match that option. So the code you have written will already accept --input as an alias for --inputs.

Related

any means of returning bool for yaml variables in yaml-cpp?

I have a config file for disabling specific code paths. I just added a bool option to the yaml file, and am having a hard time figuring out how yaml-cpp handles those. The documentation is a bit lighter than preferred, and I don't see anything for a Node that fits my use case. I could manually parse for the strings returned as true and false, but that seems like something the framework should support, as there are multiple styles of writing trueand false in the spec. Is there any means of getting a bool value out of yaml-cpp?
IsScalarwas the closest I could find.
void LoadConfig(string file)
{
Node config = LoadFile(file);
string targetDirectory;
bool compile;
if (config["TargetDirectory"])
targetDirectory = config["TargetDirectory"].Scalar();
if (config["Compile"])
compile = Config["Compile"].IsScalar();
}
You want the template as() method:
config["Compile"].as<bool>()
Or a neater way to do it all in one line instead of three using a default value (which also addresses your potential uninitialized variable bug):
bool compile = config["Compile"].as<bool>(false);

How to get clang-format to align chained method calls

I've joined an existing project and I'm the first team member to use clang-format. The existing style mostly matches except for a couple of annoying differences. Here's one (the other one being here):
folly::dynamic makeRequest(const string &response) {
return folly::dynamic::object()
("log_type", "FOO")
("src_id", "42")
("dst_id", "666")
("success", true);
}
clang-format insists on formatting it like this:
folly::dynamic makeRequest(const string &token_response) {
// using longer variable names to highlight using up the whole line lenght
return folly::dynamic::object()("log_type", "FOO")(
"src_id", somethingId)("dst_id", whateverId)("success",
sucess);
}
In the former style I don't feel strongly for how continuation lines are indented, as long as we get one method invocation per line. Is that possible?
Not the best possible solution, but you can force line breaks by putting "//" after each line:
return folly::dynamic::object() //
("log_type", "FOO") //
("src_id", "42") //
("dst_id", "666") //
("success", true);
Another approach that I have used myself is to turn off clang-format for the specific block of code.
// clang-format off
return folly::dynamic::object()
("log_type", "FOO")
("src_id", "42")
("dst_id", "666")
("success", true)
;
// clang-format on
This might not be optimal if you have more complicated logic inside the chained method params (since you will want that logic to be formatted), but if you just have a tuple like this it can be cleaner than adding empty comments.
Both ways you are bypassing clang-format, but this way is cleaner (imo) and signifies your intentions more clearly to future developers.

Extract/Identify NodeType by Name (or string - identifier)

Hi!
I'm writing a "simple" Maya command in C++, in witch I need to select from the scene (like the ls command in MEL).
But I don't know how to identify an MFn::Type data based on a string name like "gpuCache".
Actually my (very stupid) parser does a simple if that identify the MFn::Type based on two options: if the node name is "gpuCache" sets the filter using MFn::Type::kPluginShape, otherwise use kDagNode (or kShape, or whatever fits my needs for a broad identification for as many nodes as possible, for a later use of the typeName() of the MFnDagNode class).
This is the "filterByType" function, that I want to use to convert a type defined by String in a type defined by MFn::Type.
MFn::Type Switch::filterByType( MString type )
{
MFn::Type object_type;
object_type = MFn::Type::kDagNode;
MNodeClass node_class( type );
MGlobal::displayInfo( MString("Type Name: " + node_class.typeName()) );
return object_type;
}
Can someone help me, or I need to call a MEL/Python command from C++ (a thing that I really don't want to do) to get this thing done?
Thanks!

NetBeans code-template expansion; string manipulation

I'm trying to use the Code Templates feature with PHP in NetBeans (7.3), however I'm finding it rather limited. Given the following desired output:
public function addFoo(Foo $foo) {
$this->fooCollection[] = $foo;
}
I'm trying to have every instance of "foo"/"Foo" be variable; so I used a variable:
public function add${name}(${name} $$${name}) {
$this->${name}Collection[] = $$${name};
}
Of course, when expanded there isn't any regard given to the desired capitalization rules, because I can't find a way to implement that; the result being (given I populate ${name} with "Foo"):
public function addFoo(Foo $Foo) { // note the uppercase "Foo" in the argument
$this->FooCollection[] = $Foo; // and collection property names...
} // not what I had in mind
Now, I've read that NetBeans supports FreeMarker in it's templates, but that seems to be only for file-templates and not snippet-templates like these.
As far as I can tell, the FreeMarker version would look something like the following; however, it doesn't work, and ${name?capitalize} is simply seen as another variable name.
public function add${name?capitalize}(${name?capitalize} $$${name}) {
$this->${name}Collection[] = $$${name};
}
Passing "foo", allowing capitalize to fix it for type-names, second-words, etc.
Is there any way to get FreeMarker support here, or an alternative?
I'm open to any suggestions really; third-party plugins included. I just don't want to have to abandon NetBeans.
Addendum
The example given is trivial; an obvious solution for it specifically would be:
public function add${upperName}(${upperName} $$${lowerName}) {
$this->${lowerName}Collection[] = $$${lowerName};
}
Where upper/lower would be "Foo"/"foo" respectively. However, it's just an example, and I'm looking for something more robust in general (such as FreeMarker support)

Boost program options with default values always present when using vm.count()

I've been trying to validate my passed options with boost::program_options. My command has several modes, each of which have associated params that can be specified. What I'm trying to do is ensure these associated params are passed with the mode, i.e.
unicorn --fly --magic-wings-threshold
Where --fly is the mode and --magic-wings-threshold is an associated param. What I've noticed is if --magic-wings-threshold has a default value, e.g.
("magic-wings-threshold,w", po::value<double>(&wings_thresh)->default_value(0.8, "0.8"),
"Magic wings maximum power"
)
then I can't use
if (vm.count("magic-wings-threshold")( {
// do stuff
}
to detect if the user passed that param.
It appears that default value params are always passed and detected in vm.count(). Does anyone know a workaround or alternative?
use boost::program_options::variable_value::defaulted()
if (vm["magic-wings-threshold"].defaulted()) {
// assume defaulted value
} else {
// one was provided
}
If you want to tell difference between
-k option not provided
-k provided
You should use po::value()->implicit_value(), You can tell the different situations with:
-k option not provided -> vm["k"]==0
-k option provided -> vm["k"]==1