boost program_options custom validation - c++

i am trying to understand program_options custom validation in order to transform python code into c++ one.
any way
i read in the example that i have to overload validate function
i m trying to find the original function in boost program_options files but in vain
can any body just tell me where is the original validate function which i ll over load it
it is silly question but i wanna know how it validate on default to get idea about the concept of validation and how it is done
thanks in advance

Expanding on my one-line comment, I have always found boost::program_options to be a little deficient in argument validation.
As a result I have found that it's often easier to write a custom class for each option type. Normally program_options uses operator>> to decode the option value, so if you override this you get a chance to throw an exception that program_options recognises. If you use nested exceptions, you can print quite detailed error diagnostics.
Example:
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
#include <sstream>
namespace po = boost::program_options;
// a custom option type
struct foo_or_bar {
std::string value;
// self-describing
static constexpr const char *option_name() { return "foo"; }
static constexpr const char *description() { return "single option only. value must be either foo or bar"; }
// check the value and throw a nested exception chain if it's wrong
void check_value() const
try {
if (value != "foo" and value != "bar") {
std::ostringstream ss;
ss << "value must be foo or bar, you supplied " << std::quoted(value);
throw std::invalid_argument(ss.str());
}
}
catch (...) {
std::throw_with_nested(po::validation_error(po::validation_error::invalid_option_value, option_name()));
}
// overload operators
friend std::istream &operator>>(std::istream &is, foo_or_bar &arg) {
is >> arg.value;
arg.check_value();
return is;
}
friend std::ostream &operator<<(std::ostream &os, foo_or_bar const &arg) {
return os << arg.value;
}
};
// test
void test(int argc, const char **argv) {
foo_or_bar my_foo;
po::options_description desc("test options");
desc.add_options()
(foo_or_bar::option_name(), po::value(&my_foo), foo_or_bar::description());
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
std::cout << "foo is " << my_foo << std::endl;
}
void print_exception(const std::exception& e, int level = 0)
{
std::cerr << std::string(level, ' ') << "exception: " << e.what() << '\n';
try {
std::rethrow_if_nested(e);
} catch(const std::exception& e) {
print_exception(e, level+1);
} catch(...) {}
}
int main() {
{
std::vector<const char *> test_args = {
"executable_name",
"--foo=bar"
};
test(test_args.size(), test_args.data());
}
try {
std::vector<const char *> test_args = {
"executable_name",
"--foo=bob"
};
test(test_args.size(), test_args.data());
}
catch (std::exception const &e) {
print_exception(e);
}
}
expected output:
foo is bar
exception: the argument for option '--foo' is invalid
exception: value must be foo or bar, you supplied "bob"

Related

Parsing an std::vector<T> option in a config file with Boost.Program_options

It's already known that Boost.Program_options does not handle multitoken options in config files (separated by spaces) conveniently like when dealing with command-line options. The main solution that I found involves defining custom validators. As such, I defined the following validator:
template<typename T>
void validate(boost::any& v, const std::vector<std::string>& values, std::vector<T>*, int) {
std::cout << "validate called!" << std::endl;
boost::program_options::validators::check_first_occurrence(v);
if (values.size() == 0)
throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::at_least_one_value_required);
std::vector<T> numeric_values(values.size(), 0);
std::transform(values.begin(), values.end(), numeric_values.begin(), [] (const std::string&& string) {
std::istringstream iss(string);
T val;
iss >> val;
if (iss.fail())
throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value, "", string);
return val;
});
v = std::move(numeric_values);
}
However, when I try to specify an std::vector option in the config file, the validator never gets called and I get the standard error the argument ('4 8 16 32') for option 'sizes' is invalid. The relevant option line is this:
("sizes", po::value<std::vector<size_t>>(&sizes)->multitoken(), "Sizes of the different tensor modes, separated by spaces")
I suspect this solution might fail in this case because the option is of the type std::vector<T>, since I know this is handled as a special case by Boost.Program_options. I tried looking for more documentation on the use of custom validators, or check the source code, but couldn't find what I needed.
I can think of alternate solutions involving forwarding wrapper objects, which would be clearly different from the std::vector<T> type while simply forwarding the assignment operator. However, these objects would need to be passed when defining the options while still being in scope during the parsing, meaning I have to define a temporary variable for each std::vector<T> option, which doesn't seem like the best solution. I hope someone knows a better solution to this problem.
Update: I created a small reproducible example, showing that the problem only occurs with an std::vector option, not with the custom coordinate struct. Here's the source file:
#include <iostream>
#include <boost/program_options.hpp>
#include <vector>
#include <utility>
#include <fstream>
namespace po = boost::program_options;
struct coordinate {
double x, y;
};
struct options_storage {
coordinate coords = {0, 0};
std::vector<int> vector = {};
};
void print_options_storage(const options_storage& options) {
std::cout << "coords: (" << options.coords.x << ", " << options.coords.y << ")" << std::endl;
std::cout << "vector: [";
for (size_t i = 0; i < options.vector.size(); i++) {
std::cout << options.vector[i];
if (i < options.vector.size() - 1)
std::cout << ", ";
}
std::cout << "]" << std::endl;
}
template<typename T>
void validate(boost::any& v, const std::vector<std::string>& values, std::vector<T>*, int) {
std::cout << "validate-vector called!" << std::endl;
boost::program_options::validators::check_first_occurrence(v);
if (values.size() == 0)
throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::at_least_one_value_required);
std::vector<T> numeric_values(values.size(), 0);
std::transform(values.begin(), values.end(), numeric_values.begin(), [] (const std::string&& string) {
std::istringstream iss(string);
T val;
iss >> val;
if (iss.fail())
throw boost::program_options::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value, "", string);
return val;
});
v = std::move(numeric_values);
}
// From https://stackoverflow.com/questions/5884465/boostprogram-options-config-file-option-with-multiple-tokens
void validate(boost::any& v, const std::vector<std::string>& values, coordinate*, int) {
std::cout << "validate-coordinate called!" << std::endl;
coordinate c;
std::vector<double> dvalues;
for(std::vector<std::string>::const_iterator it = values.begin(); it != values.end(); ++it) {
std::stringstream ss(*it);
std::copy(std::istream_iterator<double>(ss), std::istream_iterator<double>(), std::back_inserter(dvalues));
if(!ss.eof())
throw po::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value, "", *it);
}
if (dvalues.size() != 2)
throw po::validation_error(boost::program_options::validation_error::kind_t::invalid_option_value, "", "");
c.x = dvalues[0];
c.y = dvalues[1];
v = c;
}
int main(int argc, char** argv) {
options_storage options;
po::options_description desc("General");
desc.add_options()
("coords", po::value<coordinate>(&options.coords)->multitoken())
("vector", po::value<std::vector<int>>(&options.vector)->multitoken())
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
print_options_storage(options);
std::ifstream ifs1;
ifs1.open("config1.ini", std::ifstream::in);
po::store(po::parse_config_file(ifs1, desc), vm);
po::notify(vm);
ifs1.close();
print_options_storage(options);
std::ifstream ifs2;
ifs2.open("config2.ini", std::ifstream::in);
po::store(po::parse_config_file(ifs2, desc), vm);
po::notify(vm);
ifs2.close();
print_options_storage(options);
return 0;
}
With the config files config1.ini:
coords = 3.5 4.5
config2.ini:
coords = 5.5 6.5
vector = 5 6 7 8
CMakeLists.txt:
cmake_minimum_required(VERSION 3.9)
project(boost-program-options-test LANGUAGES CXX)
set(CMAKE_VERBOSE_MAKEFILE on)
find_package(Boost COMPONENTS program_options)
add_executable(po-test test.cpp)
target_link_libraries(po-test stdc++)
target_link_libraries(po-test ${Boost_LIBRARIES})
target_compile_options(po-test PRIVATE -Wall -Wextra -Wno-unknown-pragmas -Wno-unknown-warning-option -march=native -mtune=native -O3 -DNDEBUG)
set_target_properties(po-test PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/.."
)
Output:
coords: (0, 0)
vector: []
validate-coordinate called!
coords: (3.5, 4.5)
vector: []
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::invalid_option_value> >'
what(): the argument ('5 6 7 8') for option 'vector' is invalid
Aborted (core dumped)

boost program_options values with different types

How can I provide an option to the boost program_options, which will accept 3 values with different types: [int, int, float]?
For example:
./app_name --check-integrity 1 2.2 --do_something 2 2 2.5
I've tried to achieve that with a vector of boost::any.
namespace po = boost::program_options;
po::option_descriptions desc("");
desc.add_options()
("opt", po::value<std::vector<boost::any> >()->multitoken(), "description");
But without success. This code causes:
/usr/include/boost/lexical_cast/detail/converter_lexical.hpp:243:13: error: static assertion failed: Target type is neither std::istream`able nor std::wistream`able
BOOST_STATIC_ASSERT_MSG((result_t::value || boost::has_right_shift<std::basic_istream<wchar_t>, T >::value),
Any other idea?
This is my recommended, simpler approach. I have added a true multi-token option value parsing approach as a separate answer for completeness.
You can use any streamable type. So, if you have types:
struct check {
int i;
double d;
};
struct something {
int a;
int b;
double c;
};
You can define the options like:
opt.add_options()
("do_something,s", bpo::value<something>()->default_value(something{1,2,3}, "1 2 3"), "")
("check-integrity,c", bpo::value<check>()->default_value(check{1,0.1}, "1 0.1"), "")
("help", "");
Note how you need to supply the stringified default value only if the type cannot be output-streamed (or the streamed format doesn't match the expected input format)
bpo::variables_map vm;
try {
bpo::store(parse_command_line(argc, argv, opt), vm);
bpo::notify(vm);
if (vm.count("help")) {
std::cout << opt << std::endl;
return 0;
}
std::cout << vm["do_something"].as<something>() << "\n";
std::cout << vm["check-integrity"].as<check>() << "\n";
} catch (std::exception const& e) {
std::cerr << e.what() << "\n";
}
Output Streaming: Simple Stuff
That's the usual stuff:
static inline std::ostream& operator<<(std::ostream& os, check const& v) {
return os << "check {" << v.i << ", " << v.d << "}";
}
static inline std::ostream& operator<<(std::ostream& os, something const& v) {
return os << "something {" << v.a << ", " << v.b << ", " << v.c << "}";
}
Input Streaming: Get As Fancy As You Want
Here there are some slight caveats. First of them is that the istream& supplied from program_options is - by default - modified with the std::noskipws manipulator. You have to undo that in order to be able to read space separated data.
Next up, for any non-trivial parsing/validation task I'd suggest using something more sophisticated. But for the demo, this would suffice:
static inline std::istream& operator>>(std::istream& is, check& into) {
return is >> std::skipws >> into.i >> into.d;
}
static inline std::istream& operator>>(std::istream& is, something& into) {
return is >> std::skipws >> into.a >> into.b >> into.c;
}
Demo Time
Live On Coliru
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace bpo = boost::program_options;
struct check {
int i;
double d;
};
struct something {
int a;
int b;
double c;
};
static inline std::ostream& operator<<(std::ostream& os, check const& v) {
return os << "check {" << v.i << ", " << v.d << "}";
}
static inline std::ostream& operator<<(std::ostream& os, something const& v) {
return os << "something {" << v.a << ", " << v.b << ", " << v.c << "}";
}
static inline std::istream& operator>>(std::istream& is, check& into) {
return is >> std::skipws >> into.i >> into.d;
}
static inline std::istream& operator>>(std::istream& is, something& into) {
return is >> std::skipws >> into.a >> into.b >> into.c;
}
int main(int argc, char* argv[]) {
bpo::options_description opt("all options");
opt.add_options()
("do_something,s", bpo::value<something>()->default_value(something{1,2,3}, "1 2 3"), "")
("check-integrity,c", bpo::value<check>()->default_value(check{1,0.1}, "1 0.1"), "")
("help", "");
bpo::variables_map vm;
try {
bpo::store(parse_command_line(argc, argv, opt), vm);
bpo::notify(vm);
if (vm.count("help")) {
std::cout << opt << std::endl;
return 0;
}
std::cout << vm["do_something"].as<something>() << "\n";
std::cout << vm["check-integrity"].as<check>() << "\n";
} catch (std::exception const& e) {
std::cerr << e.what() << "\n";
}
}
Prints
+ ./sotest --help
all options:
-s [ --do_something ] arg (=1 2 3)
-c [ --check-integrity ] arg (=1 0.1)
--help
+ ./sotest
something {1, 2, 3}
check {1, 0.1}
+ ./sotest --check ''
the argument for option '--check-integrity' is invalid
+ ./sotest --check oops
the argument ('oops') for option '--check-integrity' is invalid
+ ./sotest --check '8 8'
something {1, 2, 3}
check {8, 8}
+ ./sotest --do_something '11 22 .33'
something {11, 22, 0.33}
check {1, 0.1}
+ ./sotest --do_something '11 22 .33' --check '80 -8'
something {11, 22, 0.33}
check {80, -8}
I posted another answer earlier, but I can just predict the follow-up question:
But what if I want to allow --check 1 3.14 instead of --check "1 3.14" or --check '1 3.14'?
That's not a bad question, and it's actually fair because that's the close reading of the original post.
I consider this not the best strategy because:
it requires parsing into some intermediate vector of tokens
requires manual conversion from these to the target type
it makes for brittle command lines
it postpones validation of the option value until after command line parsing, the multitoken could have an unexpected number of value tokens and you wouldn't know until converting to the target type
it doesn't work at all when e.g. one of the token is negative. Because --check 8 -8 will try to parse -8 as an option
In general, it's a whole lot more work for less functionality.
HOW would you do it anyways?
Let's assume the same target types as earlier:
struct check {
int i;
double d;
friend std::ostream& operator<<(std::ostream& os, check const& v) {
return os << "check {" << v.i << ", " << v.d << "}";
}
};
struct something {
int a;
int b;
double c;
friend std::ostream& operator<<(std::ostream& os, something const& v) {
return os << "something {" << v.a << ", " << v.b << ", " << v.c << "}";
}
};
Parsing Multi-Token
Now, you suggested boost::any, but we can be more specific. We know we only expect integers or doubles:
using ArgVal = boost::variant<int, double>;
using ArgVec = std::vector<ArgVal>;
By being more specific, you catch more errors earlier (see e.g. the
parse error when supplying 'oops' as the value)
Now define the options as multi-token:
opt.add_options()
("do_something,s", bpo::value<ArgVec>()
->multitoken()
->default_value({1,2,3}, "1 2 3"), "")
("check-integrity,c", bpo::value<ArgVec>()
->multitoken()
->default_value({1,0.1}, "1 0.1"), "")
("help", "");
So far, I think that's actually pretty elegant.
Writing the rest of the demo program the way we'd like it to read:
bpo::variables_map vm;
try {
bpo::store(parse_command_line(argc, argv, opt), vm);
bpo::notify(vm);
if (vm.count("help")) {
std::cout << opt << std::endl;
return 0;
}
std::cout << as_check(vm["check-integrity"].as<ArgVec>()) << "\n";
std::cout << as_something(vm["do_something"].as<ArgVec>()) << "\n";
} catch (std::exception const& e) {
std::cerr << "ERROR " << e.what() << "\n";
}
This leaves a few loose ends:
parsing
conversion (what are as_check and as_something?)
Parsing ArgVal
Like before we can simply provide an input streaming operator for the type. And as we said back then you can "Get As Fancy As You Want".
Let's employ Spirit X3 to get a lot of mileage for little effort:
static inline std::istream& operator>>(std::istream& is, ArgVal& into) {
namespace x3 = boost::spirit::x3;
std::string arg;
getline(is, arg);
x3::real_parser<double, x3::strict_real_policies<double> > real_;
if (!phrase_parse(
begin(arg), end(arg),
(real_ | x3::int_) >> x3::eoi,
x3::blank,
into))
{
is.setstate(std::ios::failbit);
}
return is;
}
This time we embrace the noskipws because it convenes us, getting the full argument into a string.
Then, we parse it into a strict double OR an int.
By using strict real policies, we avoid parsing any integer number as a double, because later down the road we don't want to allow conversion from double to int (potentially losing information).
Converting
We need accessors to extract integer or double values. We will allow conversion from int to double since that never loses precision (--check 8 8 is as valid as --check 8 8.0).
static int as_int(ArgVal const& a) {
return boost::get<int>(a);
}
static double as_double(ArgVal const& a) {
if (auto pi = boost::get<int>(&a))
return *pi; // non-lossy conversion allowed
return boost::get<double>(a);
}
Now we can express the higher level conversions to target types in terms of the as_int and as_double helpers:
static check as_check(ArgVec const& av) {
try {
if (av.size() != 2) throw "up";
return { as_int(av.at(0)), as_double(av.at(1)) };
} catch(...) {
throw std::invalid_argument("expected check (int, double)");
}
}
static something as_something(ArgVec const& av) {
try {
if (av.size() != 3) throw "up";
return { as_int(av.at(0)), as_int(av.at(1)), as_double(av.at(2)) };
} catch(...) {
throw std::invalid_argument("expected something (int, int, double)");
}
}
I tried to write defensively, but not in particular good style. The code is safe, though.
DEMO TIME
Live On Coliru
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace bpo = boost::program_options;
struct check {
int i;
double d;
friend std::ostream& operator<<(std::ostream& os, check const& v) {
return os << "check {" << v.i << ", " << v.d << "}";
}
};
struct something {
int a;
int b;
double c;
friend std::ostream& operator<<(std::ostream& os, something const& v) {
return os << "something {" << v.a << ", " << v.b << ", " << v.c << "}";
}
};
#include <boost/spirit/home/x3.hpp>
using ArgVal = boost::variant<int, double>;
using ArgVec = std::vector<ArgVal>;
static inline std::istream& operator>>(std::istream& is, ArgVal& into) {
namespace x3 = boost::spirit::x3;
std::string arg;
getline(is, arg);
x3::real_parser<double, x3::strict_real_policies<double> > real_;
if (!phrase_parse(
begin(arg), end(arg),
(real_ | x3::int_) >> x3::eoi,
x3::blank,
into))
{
is.setstate(std::ios::failbit);
}
return is;
}
static int as_int(ArgVal const& a) {
return boost::get<int>(a);
}
static double as_double(ArgVal const& a) {
if (auto pi = boost::get<int>(&a))
return *pi; // non-lossy conversion allowed
return boost::get<double>(a);
}
static check as_check(ArgVec const& av) {
try {
if (av.size() != 2) throw "up";
return { as_int(av.at(0)), as_double(av.at(1)) };
} catch(...) {
throw std::invalid_argument("expected check (int, double)");
}
}
static something as_something(ArgVec const& av) {
try {
if (av.size() != 3) throw "up";
return { as_int(av.at(0)), as_int(av.at(1)), as_double(av.at(2)) };
} catch(...) {
throw std::invalid_argument("expected something (int, int, double)");
}
}
int main(int argc, char* argv[]) {
bpo::options_description opt("all options");
opt.add_options()
("do_something,s", bpo::value<ArgVec>()
->multitoken()
->default_value({1,2,3}, "1 2 3"), "")
("check-integrity,c", bpo::value<ArgVec>()
->multitoken()
->default_value({1,0.1}, "1 0.1"), "")
("help", "");
bpo::variables_map vm;
try {
bpo::store(parse_command_line(argc, argv, opt), vm);
bpo::notify(vm);
if (vm.count("help")) {
std::cout << opt << std::endl;
return 0;
}
std::cout << as_check(vm["check-integrity"].as<ArgVec>()) << "\n";
std::cout << as_something(vm["do_something"].as<ArgVec>()) << "\n";
} catch (std::exception const& e) {
std::cerr << "ERROR " << e.what() << "\n";
}
}
Prints
+ ./sotest --help
all options:
-s [ --do_something ] arg (=1 2 3)
-c [ --check-integrity ] arg (=1 0.1)
--help
+ ./sotest
check {1, 0.1}
something {1, 2, 3}
+ ./sotest --check ''
ERROR the argument for option '--check-integrity' is invalid
+ ./sotest --check oops
ERROR the argument ('oops') for option '--check-integrity' is invalid
+ ./sotest --check 8 8
check {8, 8}
something {1, 2, 3}
+ ./sotest --do_something 11 22 .33
check {1, 0.1}
something {11, 22, 0.33}
+ ./sotest --do_something 11 22 .33 --check 80 8
check {80, 8}
something {11, 22, 0.33}

Exception handling with stoi function

I have a little problem with exception handling in c++. I need to convert a number to string and if this string contains letters or more than 10 characters I need to give error. Here is the exception-handling part;
while ( cin >> input )
{
try
{
convert = castInput( input );
cout << "The number entered was: " << convert;
}
catch ( NonNumber &excp )
{
cout << "INVALID INPUT: " << excp.what();
}
catch ( Overflow &excp )
{
cout << "INVALID INPUT: " << excp.what();
}
cout << "\n\nPlease enter a number (end-of-file to terminate): ";
}
I used stoi function to converting the string to int but I think I need open 2 classes. I don't know why and how becase stoi fucntion own what function already.
I personally find that a regex is no overkill for this. Checking user input with a regex will have no negative impact on performance whatsoever.
But I think that you are more interested in exception handling. You need to read many many pages of a good C++ book to understand exception handling and its use cases.
And maybe, but I do not know, you want simply to catch the standard exceptions thrown by std::stoi. These are:
std::invalid_argument
std::out_of_range
That would be easy with writing
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
std::cout << e.what() << "\n";
}
catch (const std::out_of_range & e) {
std::cout << e.what() << "\n";
}
But maybe you want to learn, how to write own exceptions. For your specific requirements. But please note that your specification, e.g. "more than 10 digits" and "no letter" are maybe not quite what you want. With a machine, where int is 32bit bit, the maximum number that can be converted is: 2147483647. Any number bigger and still having only 10 characters, will throw a std::out_of_range. On the other hand numbers like 123X would be converted by std::stoi to 123. So, maybe your requirements are not so clear.
Anyway, to show you, how you could use own exceptions, I create 2 customized exceptions classes. And to make life easy, I derived those from std::exception (recommended).
See the below example:
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
class StoiException : public std::exception
{
public:
StoiException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }
protected:
void setMessage(const std::string& msg) { message = msg; }
protected:
std::string message{};
};
class NoNumber : StoiException
{
public:
NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
int castInput(std::string& input) {
int result{};
// Check, if there is any letter in the input string
if (std::any_of(input.begin(), input.end(), isalpha)) {
// If so, than throw
throw NoNumber(input);
}
// Check, if string has more than 10 characters
if (input.length() > 10) {
// If so, than throw
throw Overflow(input);
}
result = std::stoi(input);
return result;
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
try {
// Convert
int convert = castInput(input);
// This will only be shown , if there is no exception
std::cout << "\nConverted Number is: " << convert << "\n";
}
// Catch all exceptions
catch (const NoNumber & e) {
std::cout << e.what() << "\n";
}
catch (const Overflow & e) {
std::cout << e.what() << "\n";
}
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
std::cout << e.what() << "\n";
}
catch (const std::out_of_range & e) {
std::cout << e.what() << "\n";
}
}
return 0;
}
Of course you can handle the std::stoi's exception also in your custom converter function. Then only your owwn exceptions are visible.
Please see:
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
class StoiException : public std::exception
{
public:
StoiException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override { return message.c_str(); }
protected:
void setMessage(const std::string& msg) { message = msg; }
protected:
std::string message{};
};
class NoNumber : StoiException
{
public:
NoNumber(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> NoNumber: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
class Overflow : StoiException
{
public:
Overflow(const std::string& msg) : StoiException(msg) { setMessage(std::string("My Exception -> Overflow: ") + msg); }
virtual const char* what() const noexcept override { return message.c_str(); }
};
int castInput(std::string& input) {
int result{};
// Check, if there is any letter in the input string
if (std::any_of(input.begin(), input.end(), isalpha)) {
// If so, than throw
throw NoNumber(input);
}
// Check, if string has more than 10 characters
if (input.length() > 10) {
// If so, than throw
throw Overflow(input);
}
try {
result = std::stoi(input);
}
// Standard exceptions for stoi
catch (const std::invalid_argument & e) {
throw NoNumber(input);
}
catch (const std::out_of_range & e) {
throw Overflow(input);
}
return result;
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
try {
// Convert
int convert = castInput(input);
// This will only be shown , if there is no exception
std::cout << "\nConverted Number is: " << convert << "\n";
}
// Catch all exceptions
catch (const NoNumber & e) {
std::cout << e.what() << "\n";
}
catch (const Overflow & e) {
std::cout << e.what() << "\n";
}
}
return 0;
}
But maybe, what you really want to have, is a function, that encapsulates std::stoi and has an additional return value to show, if it worked or not.
Please note: The last solution will also convert "123X" to 123. That is the difference to the previous versions.
#include <iostream>
#include <string>
#include <algorithm>
#include <exception>
#include <cctype>
#include <vector>
#include <sstream>
std::pair<bool, int> castInput(std::string& input) {
bool ok{ false };
int result{};
try {
result = std::stoi(input);
ok = true;
}
// Catch stoi errors
catch (const std::invalid_argument & e) {}
catch (const std::out_of_range & e) {}
return {ok, result};
}
std::istringstream testCin{ R"(123
567
2147483647
2147483648
123X
12345678901
xyzxyzxyzxyzxyz
)" };
int main() {
std::string input{};
// Read all input
while (testCin >> input) {
const auto [ok, convert] = castInput(input);
if (ok)
std::cout << "Converted value: " << convert << "\n";
else
std::cout << "String '" << input << "' could not be converted\n";
}
return 0;
}
Why not use Regex ? You just create a regex that check if there is letters or more than 10 chars, depending on the return you process the conversion or throw an custom exception.
This may be the regex you are looking for: ^[0-9]{1,10}$
std::regex reg("^[0-9]{1,10}$");
if (std::regex_match(string, reg))
// Do what you want (process convert)
Here is the regex cpp reference : here
Edit: As commented, regex is overkill for this, so you could simply make some basic checks inside you castInput function and throw NonNumber if you find a character in the string, or throw Overflow if string.len > 10.

How to catch char * exceptions in C++

I am trying to catch a char * type exception in main() but the program crashes with the following message: terminate called after throwing an instance of 'char const*'
Here is the code:
#include <iostream>
int main ()
{
char myarray[10];
try
{
for (int n=0; n<=10; n++)
{
if (n>9)
throw "Out of range";
myarray[n]='a';
}
}
catch (char * str)
{
std::cout << "Exception: " << str << std::endl;
}
return 0;
}
Use const:
catch (const char * str)
{
std::cout << "Exception: " << str << std::endl;
}
You don't want to catch char*.
I don't know where this idea comes from that string literals are char*: they're not.
String literals are const char[N] which decays to const char*.
Catch const char*.
Your program is being terminated because, at present, you're actually not handling your exception!
Prefer an exception:
try {
for (int n=0; n<=10; n++) {
if (n>9) throw std::runtime_error("Out of range");
myarray[n]='a';
}
} catch (std::exception const& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
The C++ Standard library provides a base class specifically designed to declare objects to be thrown as exceptions. It is called std::exception and is defined in the header. This class has a virtual member function called what that returns a null-terminated character sequence (of type char *) and that can be overwritten in derived classes to contain some sort of description of the exception.
// using standard exceptions
#include <iostream>
#include <exception>
using namespace std;
class myexception: public exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main () {
try
{
throw myex;
}
catch (exception& e)
{
cout << e.what() << '\n';
}
return 0;
}
For more help: http://www.cplusplus.com/doc/tutorial/exceptions/
You cannot throw a string like that, you need to create an object.
Replace throw "Out of range" by throw std::out_of_range("Out of range")
Regards,

Correct way to declare/define custom cout-like object

I created my own std::cout-like object that writes both to std::cout and to a log file.
I'm currently defining it like this in a header file, but I'm getting unused variable warnings.
Header file <MyLib/Log.h>
static LOut { };
static LOut lo;
template<typename T> inline LOut& operator<<(LOut& mLOut, const T& mValue)
{
std::string str{toStr(mValue)};
std::cout << str;
getLogStream() << str;
return mLOut;
}
Usage:
#include <MyLib/Log.h>
...
lo << "hello!" << std::endl;
Should lo be static? Should lo be extern?
Kudos for explaining the correct way of declaring a cout-like object and showing how the main standard library implementations do it.
Edit: by cout-like object, I mean a global variable that is always available after including the corresponding header.
std::cout is simply declared as follows:
namespace std {
extern ostream cout;
}
It is a regular global variable; you can do the same thing yourself. Put an extern declaration of your variable in a header; then define the same variable in a source file and link it to your application:
// mylog.h
extern MyLog mylog;
// mylog.cpp
MyLog mylog(someparams);
First, I'm not too sure what you mean be a cout-like object?
Perhaps an std::ostream.
Anyway, the usual way of doing this is to use a filtering
streambuf. Just write a streambuf which forwards to a log file,
in addition to the usual place, and insert it where ever you
want:
class LoggingOutputStreambuf : public std::streambuf
{
std::streambuf* myDest;
std::ofstreambuf myLogFile;
std::ostream* myOwner;
protected:
int overflow( int ch )
{
myLogFile.sputc( ch ); // ignores errors...
return myDest->sputc( ch );
}
public:
LoggingOutputStreambuf(
std::streambuf* dest,
std::string const& logfileName )
: myDest( dest )
, myLogFile( logfileName.c_str(), std::ios_base::out )
, myOwner( nullptr )
{
if ( !myLogFile.is_open() ) {
// Some error handling...
}
}
LoggingOutputStreambuf(
std::ostream& dest,
std::string const& logfileName )
: LoggingOutputStreambuf( dest.rdbuf(), logfileName )
{
dest.rdbuf( this );
myOwner = &dest;
}
~LoggingOutputStreambuf()
{
if ( myOwner != nullptr ) {
myOwner->rdbuf( myDest );
}
}
};
(This is C++11, but it shouldn't be hard to modify it for
C++03.)
To use, you could use something like:
LoggingOutputStreambuf logger( std::cout );
// ...
All output to std::cout will be logged until logger goes out
of scope.
In practice, you'll likely use something more complicated than a
filebuf for logging, since you may want to insert time stamps
at the start of each line, or systematically flush at the end of
each line. (Filtering streambufs can take care of those issues
as well.)
std::cout-like object that writes both to std::cout and to a log file
Maybe boost.iostreams would be sufficient?
#include <iostream>
#include <fstream>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/tee.hpp>
namespace io = boost::iostreams;
int main()
{
typedef io::tee_device<std::ostream, std::ofstream> teedev;
typedef io::stream<teedev> LOut;
std::ofstream outfile("test.txt");
teedev logtee(std::cout, outfile);
LOut mLOut(logtee);
mLOut << "hello!" << std::endl;
}
Simply sending the input value right out to cout didn't work for me, because I wanted to add headers and info to the log output.
Also, I had my static Debug class in which to wrap the Log stream.
This is the way I managed to do this, I hope it's useful. I'm somehow a newbye to c++, so feel free to tell me if something is wrong :)
#include <iostream>
#include <sstream>
#include <ostream>
enum class DebugLevel {
INFO,
WARNING,
ERROR
};
class Debug {
public:
/* other Debug class methods/properties
...
*/
// out stream object
static struct OutStream {
std::ostringstream stream;
DebugLevel level = DebugLevel::INFO;
public:
// here you can add parameters to the object, every line log
OutStream& operator()(DebugLevel l) {
level = l;
return *this;
}
// this overload receive the single values to append via <<
template<typename T>
OutStream& operator<<(T&& value) {
stream << value;
return *this;
}
// this overload intercept std::endl, to flush the stream and send all to std::cout
OutStream& operator<<(std::ostream& (*os)(std::ostream&)) {
// here you can build the real console log line, add colors and infos, or even write out to a log file
std::cout << __TIME__ << " [" << (int)level << "] " << stream.str() << os;
stream.str(""); // reset the string stream
level = DebugLevel::INFO; // reset the level to info
return *this;
}
} Log;
};
Debug::OutStream Debug::Log; // need to be instantiaded only because I use a static Debug class
int main() {
Debug::Log(DebugLevel::ERROR) << "Hello Log! " << 2 << " " << __FUNCTION__ << std::endl;
Debug::Log << "Hello Log! " << 0xFA << std::endl; // NB: this way the debugLevel is default
return 0;
}
In one of my projects, I wrote wrapper for std::cout.
It looks something like this:
struct out_t {
template<typename T>
out_t&
operator << (T&& x) {
std::cout << x;
// log << x;
return *this;
};
};
out_t out;
out << 1;
For complete code look for struct out in io.h