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.
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'm currently creating an app to launch external apps.
The signature to launch the external apps is:
int launchApp(int argc, char** argv); // argc = amount of arguments, argv = arguments
To add arguments to a std::vector<char *> structure I use the following lambda:
auto addArgument = [](std::vector<char *> & lArguments,
const std::string & sArgument)
{
auto cstr = new char[sArgument.size() + 1];
std::copy(sArgument.cbegin(), sArgument.cend(), cstr);
cstr[sArgument.size()] = '\0';
lArguments.push_back(cstr);
};
And launching an external app:
std::vector<char *> lArguments;
addArgument(lArguments, "Argument 1");
addArgument(lArguments, "Argument 2");
launchApp(lArguments.size(),static_cast<char**>(lArguments.data());
//... Clean up arguments
How would I do this in a RAII manner instead?
I was thinking of using a std::vector<std::vector<char>> instead. However, how can I then pass the underlying raw data (char**) to launchApp()? static_cast<char**>(lArguments.data()) wouldn't work...
I usually do this in two parts:
Build a std::vector<std::string> containing the actual arguments.
Build a std::vector<const char*> where each element is the .data() of the corresponding element in the vector of strings.
The first vector, and the strings contained within it, handle your allocation, resizing, etc. while the second simply acts as an index into the memory that is being managed by the first. The lifetime of your second vector should be shorter than that of the first, which you can guarantee by wrapping both in a class.
Example:
#include <string>
#include <vector>
#include <unistd.h>
int main() {
std::vector<std::string> args = {"echo", "hello", "world"};
std::vector<const char*> argv;
argv.reserve(args.size());
for (auto& arg : args) {
argv.push_back(arg.data());
}
execvp("echo", const_cast<char**>(argv.data()));
}
(Note the ugly const_cast. Depending on how you look at it, this is either because the exec* family of functions don't follow const correctness, or because std::string::data() does not have a non-const version (prior to C++17)).
Or, with the class to wrap it all up nicely:
#include <string>
#include <vector>
#include <unistd.h>
class ExecArguments {
public:
ExecArguments(std::initializer_list<std::string> arguments)
: args(arguments) {
for (auto& arg : args) {
argv.push_back(const_cast<char*>(arg.data()));
}
}
char** data() {
return const_cast<char**>(argv.data());
}
private:
std::vector<std::string> args;
std::vector<char*> argv;
};
int main() {
ExecArguments args{"echo", "hello", "world"};
execvp(args.data()[0], args.data());
}
Collect your parameters as regular strings.
Use an inner class that:
provides a transparent implicit view to a raw array of pointers, and
takes care of managing this pointer array at the same time.
An instance of this inner class is returned by some access method. Its lifetime spans the invoking statement. See below for an example:
#include <iostream>
#include <vector>
class CollectArgs : public std::vector<std::string> {
// just for providing the raw view and taking care of its memory
class RawArgs {
std::vector<char const*> m_argv;
public:
RawArgs(std::vector<char const*> argv) {
std::swap(argv, m_argv);
}
~RawArgs() {}
public:
operator char const* const*() const { return m_argv.data(); }
};
public:
RawArgs raw_args() const {
std::vector<char const*> argv;
for(std::string const &arg : *this) argv.push_back(arg.c_str());
return argv;
}
};
// the application launcher
void call(unsigned argc, char const *const *argv) {
for(unsigned i = 0; i < argc; i++) {
std::cout << argv[i] << std::endl;
}
}
int main() {
CollectArgs args;
args.push_back("Arg1");
args.push_back("Arg2");
// create the raw view and have it destroyed immediately after this statement
call(args.size(), args.raw_args());
}
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
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.
I am trying to run the following program using boost::thread.
#include <boost/thread.hpp>
#include <iostream>
using namespace std;
class test{
public:
void hello(int i)
{
cout << i << " ";
};
};
int main(int argc, char* argv[])
{
class test t;
boost::thread thrd(t.hello, 10);
thrd.join();
return 0;
}
It is throwing an error while compiling as given below:
thread.c:17:33: error: no matching function for call to
'boost::thread::thread(, int)'
/usr/include/boost/thread/detail/thread.hpp:236:9: note: candidates
are: boost::thread::thread(F, A1) [with F = void (test::*)(int), A1 =
int] /usr/include/boost/thread/detail/thread.hpp:202:9: note:
boost::thread::thread(boost::detail::thread_move_t)
I am using boost 1.42. I have also tried old style boost::thread creation.
When hello() is not a class function, everything goes fine. Please let me know how can I fix it?
You didn't read the documentation.
You either need to make the hello method function static, or create thread by passing object of type test to it's constructor :
int main(int argc, char* argv[])
{
test t;
boost::thread thrd(&test::hello, &t, 10);
thrd2.join();
}
The problem is you are try to bind to a member function try the following (i don't have your boost version so have no idea if this works for sure)
boost::thread thrd(&test::hello, &t, 10);
Failing that you can use a binder
boost::thread thrd(
boost::bind(&test::hello, &t, 10));
If your compiler is new enough you can use the standard library equivalents of all of those by changing the boost namespace for std:: (the placeholder are in std::placeholders not the global namespace).
std::thread(... //c++11
Try with this code:
int main(int argc, char* argv[])
{
test t;
boost::thread thrd(&test::hello,&t,10);
thrd.join();
return 0;
}