In the example (regex.cpp), the author of the library created a custom struct (magic_number) and a validate function for this struct to show how custom struct can be integrated into program options. I followed his example to create a validate function for a custom class (MyClass). Compiler complains that a lexical_cast is not available for MyClass. I then implemented std::istream& operator>>(std::istream& in, MyClass& d), removed void validate(.., MyClass*, ..), the code compiles. Can anyone explain why the example doesn't require operator>>, while mine doesn't require validate?
EDIT:
#include <MyLib/MyClass.h>
std::istream& operator>>(std::istream& in, MyClass& obj) {
// some code to populate obj
return in;
}
po::variables_map parseCommandLine(int argc, char* argv[]) {
po::options_description options("Options");
options.add_options()
("help", "produce help message")
("obj", po::value<MyClass>(), "")
;
po::variables_map vm;
store(po::command_line_parser(argc, argv)
.options(options).run(), vm);
notify(vm);
return vm;
}
int main(int argc, char* argv[]) {
try {
po::variables_map vm = parseCommandLine(argc, argv);
MyClass obj = vm["my"].as<MyClass>();
cout << obj << endl;
} catch(std::exception& e) {
cout << e.what() << "\n";
return 1;
}
return 0;
}
the code compiles without validate.
I also tried making minimum change to regex.cpp:
remove magic_number
add #include <MyLib/MyClass.h>
replace all occurance of magic_number by MyClass.
comment out all code in validate.
This does not compile.
EDIT: add validate. None of them solved the compiler error.
void validate(boost::any& v,
const std::vector<std::string>& values,
std::vector<MyClass>*, int)
{
}
void validate(boost::any& v,
const std::vector<std::string>& values,
MyClass*, long)
{
}
void validate(boost::any& v,
const std::vector<std::string>& values,
MyClass*, int)
{
}
EDIT: It may relate to namespaces.
After I surrounded the validate function by namespace boost { namespace program_options { }}, the code compiled without overloading op>>. It also works if validate is put into the same namespace as MyClass. Can anyone explain this?
The basic problem you are facing is that C++ doesn't offer any facility to convent a string to an arbitrary user object (I mean without writing any code).
To solve the problem, program_options offers two possibilities:
you implement operator>>, which is the standard C++ way of doing so, but which may have impact in some other areas (i.e. you may want to parse your object in a specific way except on the command line). Internally, boost::lexical_cast is used to implement the conversion and will throw an error if op>> is not found.
you implement the validate function, which is specific to program_options but that has no impact outside option management.
I guess it uses template meta programming to find out whether you have provided validate or it will default to lexical_cast.
I can't help you why your attempt with validate failed since you didn't provide the code for it.
Here is a working example, though:
#include <boost/program_options.hpp>
#include <vector>
#include <string>
namespace po = boost::program_options;
namespace lib {
class MyClass
{
public:
int a;
};
void validate(boost::any& v,
const std::vector<std::string>& values,
MyClass*, int)
{
po::validators::check_first_occurrence(v);
const string& s = po::validators::get_single_string(values);
v = boost::any(MyClass { boost::lexical_cast<int>(s) } );
}
}
po::variables_map parseCommandLine(int argc, char* argv[])
{
po::options_description options("Options");
options.add_options()
("help", "produce help message")
("obj", po::value<lib::MyClass>(), "")
;
po::variables_map vm;
store(po::command_line_parser(argc, argv)
.options(options).run(), vm);
notify(vm);
return vm;
}
int main(int argc, char* argv[])
{
try {
po::variables_map vm = parseCommandLine(argc, argv);
lib::MyClass obj = vm["obj"].as<lib::MyClass>();
cout << obj.a << endl;
} catch(std::exception& e) {
cout << e.what() << "\n";
return 1;
}
return 0;
}
Update
With a namespace, both the class and validate must belong to the same namespace.
Related
I have a class that has to process data from various files. I thought about creating one function that will read the specified file and then also accept a call back so that it can use that to process the line. Below is an example class to represent what I am trying to do:
#include <iostream>
#include <vector>
#include <string>
class Example
{
std::vector<std::string> m_exampleFileData {
"test1",
"test2",
"test3"
};
public:
void doSomethingMain(const std::string& path)
{
processFile(path, doSomething);
}
private:
void processFile(const std::string& filePath, void (Example::*fpProcessLine)(const std::string&) )
{
for (const auto& line : m_exampleFileData) {
this->*fpProcessLine(line);
}
}
void doSomething(const std::string& line)
{
std::cout << "Hello: " << line << '\n';
}
};
int main(int argc, char** argv) {
const std::string filePath{"path"};
Example ex;
ex.doSomethingMain(filePath);
}
Compiler explorer: https://godbolt.org/z/LKoXSZ
The main issue is that no matter what I do I can't seem to be able to pass the function properly to processFile. Is there a way to do this in C++? How do I go about this?
You need to spell things out explicitly, in this situation:
processFile(path, &Example::doSomething);
Furthermore, you also need to slap on an extra pair of parenthesis, due to operator precedence:
(this->*fpProcessLine)(line);
I am trying to write an CLI using boost::program_options to an existing codebase that utilizes a lot of boost::optional arguments so I would like to parse boost::optional's from the command line. If it is not specified, the result is boost::none, and if it is specified I get an initialized value.
When I attempt to do this using a custom Boost validator, I get a bad_any_cast. Below is a MCVE of the problem.
I have a class
class MyClass {
public:
int x;
MyClass(const int a) : x(a) {};
};
and a custom Boost validator for this class. This style of validator was taken straight from the boost docs.
void validate(boost::any& v, const std::vector<std::string>& values,
MyClass* target_type, int) {
v = boost::any(boost::optional<MyClass>(boost::in_place(1)));
}
Lastly, my main function, which creates a simple parser.
#include <boost/program_options.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/utility/in_place_factory.hpp>
int main(int argc, char** argv) {
po::options_description desc("");
desc.add_options()
("MyClass", po::value<boost::optional<MyClass>>()->default_value(boost::none, ""), "MyClass");
po::variables_map args;
po::store(po::parse_command_line(argc, argv, desc), args);
}
When I do not pass the --MyClass options on the command line, the code runs successfully. However, if I pass the --MyClass option, I get a bad_any_cast
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_any_cast> >'
what(): boost::bad_any_cast: failed conversion using boost::any_cast
I stepped through with GDB and this is being thrown in the MyClass any_cast, yet if I write similar code outside of boost::program_options, it works succesfuly.
For example, the following code, that casts the same boost::optional to boost::any and then casts it back, runs without error.
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/utility/in_place_factory.hpp>
namespace po = boost::program_options;
class MyClass {
public:
int x;
MyClass(const int a) : x(a) {};
};
int f(boost::any& v) {
v = boost::any(boost::optional<MyClass>(boost::in_place(1)));
}
int main(int argc, char** argv) {
boost::any v;
f(v);
boost::any_cast<boost::optional<MyClass>>(v);
}
I know that program_options supports default_value so I could use an if statement to wrap the base value in an optional after parsing but I think it would be much cleaner to achive this using the custom validator approach above.
Does anyone have any ideas or suggestions on how to go about fixing this?
The validation function does not take an optional. This is implied by the fact that the type argument (target_type) is MyClass*, not optional<MyClass>*. docs
The function takes four parameters. The first is the storage for the value, and in this case is either empty or contains an instance of the magic_number class. The second is the list of strings found in the next occurrence of the option. The remaining two parameters are needed to workaround the lack of partial template specialization and partial function template ordering on some compilers.
Here's my take on it:
Live On Coliru
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/program_options.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <iostream>
#include <string>
namespace po = boost::program_options;
struct MyClass {
int x;
MyClass(int a) : x(a){};
friend std::ostream& operator<<(std::ostream& os, MyClass const& mc) {
return os << "MyClass(" << mc.x << ")";
}
};
void validate(boost::any &v, const std::vector<std::string> &values, MyClass * /*target_type*/, int) {
v = MyClass(std::stoi(values.front()));
}
int main(int argc, char **argv) {
po::options_description desc("");
desc.add_options()("MyClass", po::value<boost::optional<MyClass> >()->default_value(boost::none, ""), "MyClass");
po::variables_map args;
store(parse_command_line(argc, argv, desc), args);
notify(args);
std::cout << "Arg: " << args["MyClass"].as<boost::optional<MyClass> >() << "\n";
}
For:
./a.out --MyClass 42
./a.out --MyClass no_chill
./a.out
Prints
+ ./a.out --MyClass 42
Arg: MyClass(42)
+ ./a.out --MyClass no_chill
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
+ ./a.out
Arg: --
Bonus
Taking a hints from the docs I think you could make it more elegant:
int main(int argc, char **argv) {
po::options_description desc("");
boost::optional<MyClass> my_option;
desc.add_options()("MyClass", po::value(&my_option), "MyClass");
po::variables_map args;
store(parse_command_line(argc, argv, desc), args);
notify(args);
std::cout << "Arg: " << my_option << "\n";
}
Removing all the error-prone repeating of types and names.
Note: this does not work for boost versions lower than 1.65, and the user will see a static assert error coming from "lexical_cast.hpp" when compiling (see release notes for 1.65).
Live On Coliru
Still the same output.
I built an interface taking pointers to functions. Sometimes this calculation depends on state, which I want to encapsulate in a class and pass its method:
#include <iostream>
class Printer {
public:
static void print(int i) { // Want to get rid of the static
std::cout << i << "\n";
}
};
template<typename int_func>
void with_1(int_func func) {
func(1);
}
int main(int argc, char const *argv[]) {
Printer printer;
with_1(printer.print);
return 0;
}
I need non-static methods (and would even prefer overloading operator()). However removing the static results in error: a pointer to a bound function may only be used to call the function.
I could use a dummy like this:
Printer printer;
void dummy(int i) {
printer.print(i);
}
int main(int argc, char const *argv[]) {
with_1(dummy);
return 0;
}
But that does not look elegant to me. Can I write a template that accepts both, function pointers and non-static methods? Or is there even a better design pattern for my problem?
You can not simply pass non-static method like this, because they work on instance. A simply solution is to use lambda:
#include <iostream>
class Printer {
public:
static void print(int i) { // Want to get rid of the static
std::cout << i << "\n";
}
};
template<typename int_func>
void with_1(int_func func) {
func(1);
}
int main(int argc, char const *argv[]) {
Printer printer;
// Can use capture by reference because we are sure printer still
// exist during execution of with_1
with_1([&printer](int i){ printer.print(i); });
return 0;
}
example
Try this:
int main(int argc, char const *argv[]) {
Printer printer;
with_1( std::bind( &Printer::print, printer, std::placeholders::_1 ) );
return 0;
}
(You'll need to #include <functional>.)
I am trying to use a custom validator instead of overloading operator>> to support an enum type in my option parsing. I have done the following:
#include <iostream>
#include <boost/program_options.hpp>
enum class MyEnum
{
OneThing,
AnotherThing
};
void validate(boost::any& v, const std::vector<std::string>& values,
MyEnum*, int)
{
// parse the enum from values[0]
std::cout << "Test" << std::endl;
MyEnum enumValue = MyEnum::OneThing;
v = enumValue;
}
int main(int argc, char*argv[])
{
namespace po = boost::program_options;
po::options_description desc("Options");
//desc.add_options()("myEnum", po::value<MyEnum>(), "MyEnum value"); // works fine
desc.add_options()("myEnum", po::value<MyEnum>()->default_value(MyEnum::OneThing), "MyEnum value"); // compiler error: Source type is not streamable
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
MyEnum myEnum = vm["myEnum"].as<MyEnum>();
return 0;
}
This works fine as long as I don't try to set a default_value, but when I do specify a default_value, I get error: static assertion failed: Source type is neither std::ostream able nor std::wostream able. What else do I need (without overloading stream operators, which is the whole point of using a validator as far as I understand) to do to allow a custom type to get a default_value?
You can set a default value, however, it needs to know how to represent the default value in the descriptions.
Since the enum isn't streamable, the automatic way doesn't work, so you must specify it, e.g.:
default_value(MyEnum::OneThing, "OneThing")
Live On Coliru
#include <iostream>
#include <boost/program_options.hpp>
enum class MyEnum
{
OneThing,
AnotherThing
};
void validate(boost::any& v, const std::vector<std::string>& values,
MyEnum*, int)
{
// parse the enum from values[0]
std::cout << "Test" << std::endl;
MyEnum enumValue = MyEnum::OneThing;
v = enumValue;
}
int main(int argc, char*argv[])
{
namespace po = boost::program_options;
po::options_description desc("Options");
//desc.add_options()("myEnum", po::value<MyEnum>(), "MyEnum value"); // works fine
desc.add_options()("myEnum", po::value<MyEnum>()->default_value(MyEnum::OneThing, "OneThing"), "MyEnum value");
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
MyEnum myEnum = vm["myEnum"].as<MyEnum>();
std::cout << std::boolalpha << (myEnum == MyEnum::OneThing) << "\n";
}
Prints
true
Is there any way I can allow multiple occurrences of custom type (struct) in boost::program_options? I found the various sources specifying this can be done using std::vector but I wanted to achieve the same using custom data type. However this struct do contain a std::vector where I want to store the data.
A code sample would really help a lot.
But, since your struct will contain the vector, why not bind that vector?
Simple example:
Live On Coliru
#include <boost/program_options.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/cmdline.hpp>
#include <iostream>
#include <vector>
struct my_custom_type {
std::vector<std::string> values;
friend std::ostream& operator<<(std::ostream& os, my_custom_type const& mct) {
std::copy(mct.values.begin(), mct.values.end(), std::ostream_iterator<std::string>(os << "{ ", ", "));
return os << "}";
};
};
int main(int argc, char** argv) {
namespace po = boost::program_options;
my_custom_type parse_into;
po::options_description desc;
desc.add_options()
("item", po::value<std::vector<std::string> >(&parse_into.values), "One or more items to be parsed")
;
try {
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc, po::command_line_style::default_style), vm);
vm.notify();
std::cout << "Parsed custom struct: " << parse_into << "\n";
} catch(std::exception const& e) {
std::cerr << "Error: " << e.what() << "\n";
}
}
When called with 26 arguments, like ./test --item={a..z} it prints:
Parsed custom struct: { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, }
If you want to "automatically" treat the conversions in a "special" way, you can look at
custom validators (http://www.boost.org/doc/libs/1_59_0/doc/html/program_options/howto.html#idp343336928)
custom notifiers (see e.g. Example 63.1 for a simple example).