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
Related
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.
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).
Even though in a template I can have any type, the function to_string does not work on basic strings:
for example:
std::string str("my string");
my_class(str);
with this functor definition:
template<class valuetype>
void operator()(valuetype value)
{
...
private_string_field = std::to_string(value);
does not work. here is the error:
error: no matching function for call to
‘to_string(std::basic_string&)’
What is the best way to avoid it.
In advance, I request avoid linking to irrelevant questions just because of a few common keywords.
std::to_string only works on fundamental numeric types.
If you need a more generic function, boost::lexical_cast will work on many more types - effectively any type that can be sent to an iostream.
#include <boost/lexical_cast.hpp>
...
private_string_field = boost::lexical_cast<std::string>(value);
There is no to_string for basic string. It would have nothing to do.
After Benjamin Lindley's suggestion I would consider the following design, use to_string but provide default template:
#include <iostream>
#include <string>
struct Type {
explicit operator std::string() const{
return std::string("I am type");
}
};
namespace std {
template <typename T>
string to_string(const T& value) {
return string(value);
}
}
int main(int argc, char **argv) {
// this is what would be in class
Type x;
std::string private_string_field;
private_string_field = std::to_string(42);
std::cout << private_string_field << std::endl;
private_string_field = std::to_string(x);
std::cout << private_string_field << std::endl;
return 0;
}
By default it tries to cast the operand to a string. This way custom types can provide their own conversion. Alternative design would be to internally use stringstream and operator<< for conversions, like this:
#include <iostream>
#include <string>
#include <sstream>
struct Type {
friend std::ostream& operator<<(std::ostream& out, const Type& value){
return out << "Type through operator<<";
}
};
template <class T>
std::string to_str(const T& value) {
std::string ret;
std::ostringstream ss;
ss << value;
ret = ss.str();
return ret;
};
int main(int argc, char **argv) {
// this is what would be in class
Type x;
std::string private_string_field;
private_string_field = to_str(42);
std::cout << private_string_field << std::endl;
private_string_field = to_str(x);
std::cout << private_string_field << std::endl;
return 0;
}
I'm create my own LOGGER, where I use an additional class for overload macro.
There is #define qlcd MacroCall(QLC::Debug), so i can use logger like this: qlcd << message;
It's ok, but when i try use qlcd("log name") i got an error. Look minimal code (no macro for simplify):
#include <QVariant>
#include <QDebug>
class QLC
{
public:
// Error types
enum LevelType{
Debug=0, // Debug
Error=1, // Error
WTF = 2 // WTF???
} level;
QString logger;
// Constructors
QLC(QLC::LevelType l)
:level(l), logger(":")
{}
QLC(QLC::LevelType l, QString log)
:level(l), logger(log)
{}
// OPERATOR <<
QLC& operator<<(const QVariant var){
qDebug() << "(" + QString::number(level) + ")" << logger << var;
}
};
class MacroCall
{
QLC::LevelType level;
public:
MacroCall()
:level(QLC::Debug){}
MacroCall(int i)
:level(QLC::WTF){}
MacroCall(QLC::LevelType l)
:level(l){}
QLC operator()(){
return QLC(level);
}
QLC operator()(QString log){
return QLC(level, log);
}
};
int main(int argc, char*argv[])
{
MacroCall()("WorkLog") << "No level, yes logname";
MacroCall(QLC::Error)() << "No logname, yes level";
MacroCall a(QLC::Error);
a("WorkLog") << "Logname and level at different lines";
// GET READY!
// INT as level and logname:
MacroCall(2)("WorkLog") << "WTF?? It works!";
//MacroCall(QLC::WTF)("NotWorkLog") << "It's not work!!!!!!";
// NOT WORK: error: invalid use of qualified-name 'QLC::WTF'
// Qt 4.8.3
return 0;
}
The code
MacroCall(QLC::WTF)("NotWorkLog")
is interpreted as declaration of a variable:
MacroCall QLC::WTF("NotWorkLog")
Example:
class A
{
public:
enum E {
x=1
};
public:
A(E) {}
void operator()(int) const { }
};
class B {};
void f()
{
(A(A::x))(1); // Ok
A{A::x}(1); // Ok
A(a)(A::x); // Ok
A::E a; // ‘a’ has a previous declaration as ‘A a’
A(A::x)(1); // invalid use of qualified-name ‘A::x’
B(b)(A::x); // no matching function for call to ‘B::B(A::E)’
}
The code you gave compiles (except that the method QLC& operator<<(const QVariant var) has to return something), eventhough I'm not sure of how it's supposed to be used.
My guess is that your 2 classes are defined in different headers and an include is missing. Does Macrocall header include QLC header ?
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.