I have BOOST installed on my Windows PC with Mingw. The BOOST installation basically works, but when I use a macro I got following error message:
macro "BOOST_PP_NOT_EQUAL_1" defined here
Under Linux it works!
Anybody out there what can help me?
Or at least give some hint.
Here are my marco's:
#include <iostream>
#include <ostream>
#include <set>
#include <boost/preprocessor.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
class EnumShowValue {
private:
static bool showValueFlag;
public:
explicit EnumShowValue(const bool flag) { EnumShowValue::showValueFlag = flag; }
static bool showValue() { return EnumShowValue::showValueFlag; }
};
inline std::ostream &operator <<(std::ostream &os, const EnumShowValue &) { return os; }
// Defines a entry of the enumerations either with an initialization value or with a default value.
#define ENUM_FIELD(C, T, ARGS)\
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(ARGS), 2),\
BOOST_PP_TUPLE_ELEM(0, ARGS) = BOOST_PP_TUPLE_ELEM(1, ARGS),\
BOOST_PP_TUPLE_ELEM(0, ARGS)),
// Define output of a single enumeration value.
#define ENUM_OUTPUT_CASE(C, T, ARGS)\
case T::BOOST_PP_TUPLE_ELEM(0, ARGS):\
os << BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0, ARGS));\
break;
#define ENUM_INSERT(C, T, ARGS)\
this->insert(T::BOOST_PP_TUPLE_ELEM(0, ARGS));
#define ENUM_DEFINE_SET(C, T, ARGS)\
class Collection : public std::set<C> {\
public:\
Collection() : std::set<C>() {\
BOOST_PP_SEQ_FOR_EACH(ENUM_INSERT, C, ARGS)\
}\
};\
Collection collection;
// Works only if the enumeration values are unique.
#define ENUM(C, T, ARGS) \
enum class C : T {\
BOOST_PP_SEQ_FOR_EACH(ENUM_FIELD, C, ARGS)\
};\
ENUM_DEFINE_SET(C, T, ARGS)\
std::ostream &operator <<(std::ostream &os, const C val);\
std::ostream &operator <<(std::ostream &os, const C val) {\
switch (val) {\
BOOST_PP_SEQ_FOR_EACH(ENUM_OUTPUT_CASE, C, ARGS)\
default:\
os << "illegal value: " << BOOST_PP_STRINGIZE(C);\
if (!EnumShowValue::showValue()) {\
os << '(';\
if (sizeof(T) == 1) {\
os << static_cast<int>(val);\
} else {\
os << static_cast<T>(val);\
}\
os << ')';\
}\
}\
if (EnumShowValue::showValue()) {\
os << '(';\
if (sizeof(T) == 1) {\
os << static_cast<int>(val);\
} else {\
os << static_cast<T>(val);\
}\
os << ')';\
}\
return os;\
}
and a example how to use:
(it defines some logging levels)
namespace logging {
ENUM(Level, unsigned char, ((DEBUG)(INFO)(WARNING)(ERR)))
}
If found a solution show below.
What I was not able so solve is a more general solution in with I can define the name of the enumeration, the basic type and the values in a #define or in an template.
Has anybody an idea how to do this?
#include <cstdint>
#include <iostream>
#include <ostream>
#define Days\
X(Monday, = 100)\
X(Tuesday,)\
X(Wednesday,)\
X(Thursday,)\
X(Friday,)\
X(Saturday, = 110)\
X(Sunday,)
enum class Weekdays : uint8_t {
#define X(a, b) a b,
Days
#undef X
};
std::ostream &operator <<(std::ostream &os, const Weekdays e);
std::ostream &operator <<(std::ostream &os, const Weekdays e) {
switch (e) {
#define X(a, b) case Weekdays::a: os << #a; break;
Days;
#undef X
default:
os << "invalid value: Weekdays=" << uint64_t(e);
}
return os;
}
int main(const int /*argc*/, const char *const /*argv*/[]) {
std::cout << Weekdays::Monday << std::endl;
std::cout << Weekdays::Tuesday << std::endl;
std::cout << Weekdays::Saturday << std::endl;
std::cout << Weekdays(99) << std::endl;
return EXIT_SUCCESS;
}
Related
A parser application where I’m working on calls for recursive rules. Besides looking into the Recursive AST tutorial examples of Boost Spirit X3 which can be found here:
https://www.boost.org/doc/libs/develop/libs/spirit/doc/x3/html/index.html, I was looking for a solution with a std::variant of some types as well as a std::vector of that same
variant type.
In the StackOverflow post titled: Recursive rule in Spirit.X3, I found the code from the answer from sehe a decent starting point for my parser.
I have repeated the code here but I have limited the input strings to be tested. Because the full list from the original is not relevant for this question here.
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>
struct value: std::variant<int,float,std::vector<value>>
{
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
friend std::ostream& operator<<(std::ostream& os, base_type const& v) {
struct {
std::ostream& operator()(float const& f) const { return _os << "float:" << f; }
std::ostream& operator()(int const& i) const { return _os << "int:" << i; }
std::ostream& operator()(std::vector<value> const& v) const {
_os << "tuple: [";
for (auto& el : v) _os << el << ",";
return _os << ']';
}
std::ostream& _os;
} vis { os };
return std::visit(vis, v);
}
};
namespace parser {
namespace x3 = boost::spirit::x3;
x3::rule<struct value_class, value> const value_ = "value";
x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
x3::real_parser<float, x3::strict_real_policies<float> > float_;
const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
const auto value__def
= "float" >> (':' >> float_)
| "int" >> (':' >> x3::int_)
| o_tuple_
;
BOOST_SPIRIT_DEFINE(value_, o_tuple_)
const auto entry_point = x3::skip(x3::space) [ value_ ];
}
int main()
{
for (std::string const str : {
"float: 3.14",
"int: 3",
"tuple: [float: 3.14,int: 3]",
"tuple: [float: 3.14,int: 3,tuple: [float: 4.14,int: 4]]"
}) {
std::cout << "============ '" << str << "'\n";
//using boost::spirit::x3::parse;
auto first = str.begin(), last = str.end();
value val;
if (parse(first, last, parser::entry_point, val))
std::cout << "Parsed '" << val << "'\n";
else
std::cout << "Parse failed\n";
if (first != last)
std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
}
}
However I would like to use a traditional visitor class rather than making ostream a friend in the variant class. You know just a struct/class with a bunch of function objects for each type you encounter in the variant and a "for loop" for the vector that calls std::visit for each
element.
My goal for the traditional visitor class is to be able to maintain a state machine during printing.
My own attempts to write this visitor class did fail because I ran into an issue with my GCC 8.1 compiler. With GCC during compilation std::variant happens to be std::variant_size somehow and I got the following error:
error: incomplete type 'std::variant_size' used in nested name specifier
More about this here:
Using std::visit on a class inheriting from std::variant - libstdc++ vs libc++
Is it possible giving this constraint on GCC to write a visitor class for the code example I included, so that the ostream stuff can be removed?
Is it possible giving this constraint on GCC to write a visitor class for the code example I included, so that the ostream stuff can be removed?
Sure. Basically, I see three approaches:
1. Add the template machinery
You can specialize the implementation details accidentally required by GCC:
struct value: std::variant<int,float,std::vector<value>> {
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
};
namespace std {
template <> struct variant_size<value> :
std::variant_size<value::base_type> {};
template <size_t I> struct variant_alternative<I, value> :
std::variant_alternative<I, value::base_type> {};
}
See it live on Wandbox (GCC 8.1)
2. Don't (again live)
Extending the std namespace is fraught (though I think it's legal for
user-defined types). So, you can employ my favorite pattern and hide th
estd::visit dispatch in the function object itself:
template <typename... El>
void operator()(std::variant<El...> const& v) const { std::visit(*this, v); }
Now you can simply call the functor and it will automatically dispatch
on your own variant-derived type because that operator() overload does
NOT have the problems that GCC stdlib has:
if (parse(first, last, parser::entry_point, val))
{
display_visitor display { std::cout };
std::cout << "Parsed '";
display(val);
std::cout << "'\n";
}
3. Make things explicit
I like this the least, but it does have merit: there's no magic and no
tricks:
struct value: std::variant<int,float,std::vector<value>> {
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
base_type const& as_variant() const { return *this; }
base_type& as_variant() { return *this; }
};
struct display_visitor {
void operator()(value const& v) const { std::visit(*this, v.as_variant()); }
// ...
Again, live
SUMMARY
After thinking a bit more, I'd recommend the last approach, due to the relative simplicity. Clever is often a code-smell :)
Full listing for future visitors:
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>
struct value: std::variant<int,float,std::vector<value>> {
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
base_type const& as_variant() const { return *this; }
base_type& as_variant() { return *this; }
};
struct display_visitor {
std::ostream& _os;
void operator()(value const& v) const { std::visit(*this, v.as_variant()); }
void operator()(float const& f) const { _os << "float:" << f; }
void operator()(int const& i) const { _os << "int:" << i; }
void operator()(std::vector<value> const& v) const {
_os << "tuple: [";
for (auto& el : v) {
operator()(el);
_os << ",";
}
_os << ']';
}
};
namespace parser {
namespace x3 = boost::spirit::x3;
x3::rule<struct value_class, value> const value_ = "value";
x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
x3::real_parser<float, x3::strict_real_policies<float> > float_;
const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
const auto value__def
= "float" >> (':' >> float_)
| "int" >> (':' >> x3::int_)
| o_tuple_
;
BOOST_SPIRIT_DEFINE(value_, o_tuple_)
const auto entry_point = x3::skip(x3::space) [ value_ ];
}
int main()
{
for (std::string const str : {
"float: 3.14",
"int: 3",
"tuple: [float: 3.14,int: 3]",
"tuple: [float: 3.14,int: 3,tuple: [float: 4.14,int: 4]]"
}) {
std::cout << "============ '" << str << "'\n";
//using boost::spirit::x3::parse;
auto first = str.begin(), last = str.end();
value val;
if (parse(first, last, parser::entry_point, val))
{
display_visitor display { std::cout };
std::cout << "Parsed '";
display(val);
std::cout << "'\n";
}
else
std::cout << "Parse failed\n";
if (first != last)
std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
}
}
I fail to compile code in debug mode (code with BOOST_SPIRIT_DEBUG_NODE(my_rule)) when my_rule has some local variable of custom type.
First version with rule qi::locals<std::string> is OK
Second version with rule qi::locals<std::string,int> is still OK
Current version with rule qi::locals<std::string,std::vector<int> > does not compile.
error: no match for operator<< (operand types are std::basic_ostream<char> and const std::vector<int>)
I declare streaming operator<< :
std::ostream& > operator<< (std::ostream& os, std::vector<int> const& art)
But It still does not compile.
I use boost 1_64_0. Here is the smallest complete code:
#define BOOST_SPIRIT_DEBUG
#if !defined(BOOST_SPIRIT_DEBUG_OUT)
#define BOOST_SPIRIT_DEBUG_OUT std::cerr
#endif
#include <tuple>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <iostream>
#include <string>
#include <vector>
// To solve the pb of declaration of grammar with locals
#include <typeinfo>
std::ostream&
operator<< (std::ostream& os, std::vector<int> const& art)
{
os << "[";
for( auto it = art.begin(); it != art.end() ; it++ ) {
os << *it << ",";
}
os << "]";
return os;
}
namespace client
{
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using phoenix::val;
using namespace qi::labels;
using qi::_val;
using qi::_1;
// Our number list parser
template <typename Iterator>
struct mini_wkart_grammar
// first version: : qi::grammar<Iterator, int(), qi::locals<std::string>, ascii::space_type>
// second version: : qi::grammar<Iterator, int(), qi::locals<std::string,int>, ascii::space_type>
: qi::grammar<Iterator, std::vector<int>(), qi::locals<std::string,std::vector<int> >, ascii::space_type>
{
mini_wkart_grammar() : mini_wkart_grammar::base_type(start,"numbers")
{
using phoenix::push_back;
// first version: start= (qi::int_ >> qi::char_(',') >> qi::int_)[_val=_1+_3];
// second version: start= (qi::int_[_b=_1] >> qi::char_(',') >> qi::int_[_b+=_1])[_val=_b];
start= (qi::int_[push_back(_b,_1)] >> qi::char_(',') >> qi::int_[push_back(_b,_1)])[_val=_b];
BOOST_SPIRIT_DEBUG_NODE(start);
}
// first version OK: qi::rule<Iterator, int(), qi::locals<std::string>, ascii::space_type> start;
// second version OK: qi::rule<Iterator, int(), qi::locals<std::string,int>, ascii::space_type> start;
qi::rule<Iterator, std::vector<int>(), qi::locals<std::string,std::vector<int> >, ascii::space_type> start;
};
}
////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA comma separated list parser for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a comma separated list of numbers.\n";
std::cout << "Type [q or Q] to quit\n\n";
// std::string result;
// first ans second version: int result;
std::vector<int> result;
std::string str;
using boost::spirit::ascii::space;
client::mini_wkart_grammar<std::string::const_iterator> wkart_grammar;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
// if (client::parse_numbers(str.begin(), str.end()))
if (boost::spirit::qi::phrase_parse(iter, end, wkart_grammar, space, result))
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << result << " Parses OK: " << std::endl;
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
I think miss something in the operator declaration?
Thanks for any help..
First Off...
What are you trying to achieve anyways? That whole grammar could be start = qi::int_ % ','; and still have the exact same effect. See Boost Spirit: “Semantic actions are evil”?
Your Question:
Sadly you need to make that operator<< ADL-enabled. (http://en.cppreference.com/w/cpp/language/adl)
Since the element type is primitive, there is no associated namespace. So the only namespace that will be tried is namespace ::std which declared std::vector<>.
namespace std {
std::ostream &operator<<(std::ostream &os, vector<int> const &art) {
os << "[";
for (auto it = art.begin(); it != art.end(); it++) {
os << *it << ",";
}
os << "]";
return os;
}
}
That might have undesired side effects, you you may want to force the issue with a hack:
namespace ADL_Hack {
template <typename T>
struct allocator : std::allocator<T> { };
}
template <typename T>
using Vector = std::vector<T, ADL_Hack::allocator<T> >;
namespace ADL_Hack {
template <typename... Ts>
std::ostream &operator<<(std::ostream &os, std::vector<Ts...> const &art) {
os << "[";
for (auto it = art.begin(); it != art.end(); it++) {
os << *it << ",";
}
os << "]";
return os;
}
}
See it Live On Wandbox
I overloaded the Extraction and Insertion operators. But now whenever they're being called in main.cpp the compiler tells me that there's an ambuiguity error. I sorta understand what ambiguity errors are, but I can't for the life of me figure out why it's happening.
I'm using g++ compiler and LLDB debugger if it helps.
The Error:
/Users/Final Project/ms1.cpp:147:12: error: use of overloaded operator '<<' is ambiguous (with operand types 'ostream' (aka 'basic_ostream<char>') and 'oop244::Date')
cout << A << " is not equal to " << B << " but operator!= returns otherwise!" << endl;
~~~~ ^ ~
/Users/Final Project/Date.h:57:17: note: candidate function
std::ostream& operator<<(std::ostream os, Date& Dob);
Followed by a billion other similar errors.
My main (where the error occurs):
#include <iostream>
#include <cstring>
#include "Date.h"
#include "Date.cpp"
#include "general.h"
using namespace std;
using namespace oop244;
bool equalDates(const char* A, const char* B);
int main(){
int ret = 0;
char confirm[2000];
bool ok = true;
Date A(2018, 10, 18);
Date B;
......
......
A = Date(2018, 10, 17);
if (ok){
cout << "testing operator!=" << endl;
if (A != B){
cout << "passed!" << endl;
}
else{
cout << A << " is not equal to " << B << " but operator!= returns otherwise!" << endl;
ok = false;
}
}
Class declaration
#ifndef __244_DATE_H__
#define __244_DATE_H__
// header file includes
#include "general.h"
#include <string>
#include <iostream>
namespace oop244{
class Date{
private:
// private member variables
int _year;
int _mon;
int _day;
int _readErrorCode;
// private member functions and setters
int value()const;
void setErrCode(int errCode);
.....
.....
public:
// constructors
Date();
Date(int,int,int);
~Date();
std::ostream& write(std::ostream& ostr, Date& dob);
std::istream& read(std::istream& istr, Date& dob);
};
std::istream& operator>>(std::istream is, Date& dob);
std::ostream& operator<<(std::ostream os, Date& dob);
}
#endif
Class function definitions
#include "general.h"
#include "Date.h"
#include <iomanip>
#include <iostream>
using namespace std;
namespace oop244{
// constructors
Date::Date()
{
_year=0;
_mon=0;
_day=0;
_readErrorCode=NO_ERROR;
}
Date::Date(int year,int month, int day)
{
_year=year;
_mon=month;
_day=day;
_readErrorCode=NO_ERROR;
}
Date::~Date() {}
// member functions
void Date::setErrCode(int errCode)
{
_readErrorCode=errCode;
}
void Date::passErr(int errCode)
{
setErrCode(errCode);
}
.....
.....
int Date::getErrCode() const
{
return _readErrorCode;
}
std::istream& Date::read(std::istream& istr, Date& dob)
{
char y[5],m[3],d[3];
int year,month,day;
istr.getline(y,5,'/');
istr.getline(m,3,'/');
istr.getline(d,3,'/');
year=atoi(y);
month=atoi(m);
day=atoi(d);
Date temp (year,month,day);
if (year>=2000 && year<=2030)
dob.passErr(YEAR_ERROR);
else if (month+=1 && month<=12)
dob.passErr(MON_ERROR);
else if (day>=1 && day<=31)
dob.passErr(DAY_ERROR);
else
{
dob.passErr(NO_ERROR);
dob=temp;
}
return istr;
}
std::ostream& Date::write(std::ostream& ostr, Date& dob)
{
int year=dob.getYear();
int month=dob.getMonth();
int day=dob.getDay();
ostr<<year<<"/"<<month<<"/"<<day<<endl;
return ostr;
}
// non-memeber operator overloads
std::istream& operator>>(std::istream& is, Date& dob)
{
dob.read(is,dob);
return is;
}
std::ostream& operator<<(std::ostream& os, Date& dob)
{
dob.write(os,dob);
return os;
}
}
Sorry for the super long post. I'm kinda at the end of my wits. My thank to you guys in advance.
Looks like the function prototypes for operator>> and operator<< don't match their parameters with the definitions. In other words:
std::ostream& operator<<(std::ostream os, Date& dob);
in your header file is missing the & for the std::ostream. Same for operator>>.
The following sample code works fine under linux using g++4.8.2, using boost1_56. However, I get a strange linker error under MacOS X (Yosemite) using clang:
ld: internal error: atom not found in symbolIndex(__ZNSt3__112__hash_tableINS_17__hash_value_typeIKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEN15FRUIT_TUPLES4dataEEENS_22__unordered_map_hasherIS8_SB_NS9_8key_hashELb1EEENS_21__unordered_map_equalIS8_SB_NS9_9key_equalELb1EEENS5_ISB_EEE15__insert_uniqueIRKNS_4pairIS8_SA_EEEENSL_INS_15__hash_iteratorIPNS_11__hash_nodeISB_PvEEEEbEEOT_) for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Proces
The main.cpp file
#include "TupleFruits.hpp"
int main()
{
map_t fruitHash = InitializeFruitHash();
std::string fruit = "BANANA";
auto itr = fruitHash(fruit);
if (fruitHash.end() == itr)
{
std::cout << fruit << " not found in hash" << std::endl;
exit(1);
}
}
The FruitHash.cpp file:
#include "TupleFruits.hpp"
map_t InitializeFruitHash()
{
static map_t m;
data dBANANA = {0, 0, 6, false};
data dGRAPEFRUIT = {1, 1, 6, false};
data dSTRAWBERRY = {2, 2, 6, false};
m[BANANA] = dBANANA;
m[GRAPEFRUIT] = dGRAPEFRUIT;
m[STRAWBERRY] = dSTRAWBERRY;
return m;
}
The include file "HashData.hpp
#ifndef HASH_DATA_HPP
#define HASH_DATA_HPP
#include <string>
#include <unordered_map>
#include <cstring>
#include <iostream>
#include <tuple>
#include <boost/functional/hash.hpp>
typedef std::string fruit_key_t;
namespace HASH_TUPLES
{
struct key_hash : public std::unary_function<fruit_key_t, std::size_t>
{
std::size_t operator()(const fruit_key_t& k) const
{
std::hash<std::string> hash_fn;
return hash_fn(k);
}
};
struct key_equal : public std::binary_function<fruit_key_t, fruit_key_t, bool>
{
bool operator()(const fruit_key_t& v0, const fruit_key_t& v1) const
{
return (v0 == v1);
}
};
struct data
{
int row;
int column;
int precision;
bool isRipe;
inline bool operator ==(data d)
{
if (d.row == row && d.column == column)
return true;
else
return false;
}
friend std::ostream& operator << (std::ostream& os, const data& rhs) //Overloaded operator for '<<'
{ //for struct output
os << rhs.row << ", "
<< rhs.column;
return os;
}
};
typedef std::unordered_map<const fruit_key_t, data, key_hash, key_equal> map_t;
// ^ this is our custom hash
}
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type,
typename T::mapped_type,
bool>
{
public:
bool operator() (typename T::value_type &pair,
typename T::mapped_type i) const
{
return pair.second == i;
}
};
#endif
The include file "TupleFruits.hpp"
#ifndef TUPLESFRUITS_HPP
#define TUPLESFRUITS_HPP
#include <boost/interprocess/containers/string.hpp>
#include "HashData.hpp"
using namespace HASH_TUPLES;
map_t InitializeFruitHash();
static std::string BANANA = "banana";
static std::string GRAPEFRUIT = "grapefruit";
static std::string STRAWBERRY = "strawberry";
#endif
I figured it out. Somehow -s (strip all symbols from binary) snuck in my Makefile
I am trying to parse a list input from the command line.
My class is derived from vector
The compiler complains about a overloaded validate being ambiguous.
I can see why, but do not know how to solve this issue.
Please help.
Below is a minimal example that generates the error. If the type of ch_list is changed to a vector, this minimal example compiles.
// g++ -std=c++11 -Wall -Wextra -pedantic test.cpp -o test -lboost_program_options -lboost_system
#include <vector>
#include <boost/program_options.hpp>
#include <iostream>
#include <vector>
using namespace std;
class mylist : public vector<int> {
public:
friend istream &operator>>(istream &is, mylist& l) {
int val;
is >> val;
l.push_back(val);
return is;
}
friend ostream& operator<<(ostream& os, const mylist& l) {
return os << l[0];
}
};
int main (int argc, char* argv[])
{
//vector<int> ch_list; // This works
mylist ch_list; // This doesn't
namespace po = boost::program_options;
po::options_description desc("Allowed options");
desc.add_options()
("ch", po::value<decltype(ch_list)>(&ch_list), "Set channel numbers")
;
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
}
catch (po::error& e) {
cerr << "ERROR: " << e.what() << "\n";
return EXIT_FAILURE;
}
if (!ch_list.empty())
cout << ch_list[0] << "\n";
return EXIT_SUCCESS;
}
I get these errors
/usr/include/boost/program_options/detail/value_semantic.hpp: In instantiation of ‘void boost::program_options::typed_value<T, charT>::xparse(boost::any&, const std::vector<std::basic_string<charT> >&) const [with T = mylist; charT = char]’:
test.cpp:47:5: required from here
/usr/include/boost/program_options/detail/value_semantic.hpp:169:13: error: call of overloaded ‘validate(boost::any&, const std::vector<std::basic_string<char> >&, mylist*, int)’ is ambiguous
/usr/include/boost/program_options/detail/value_semantic.hpp:169:13: note: candidates are:
/usr/include/boost/program_options/detail/value_semantic.hpp:81:10: note: void boost::program_options::validate(boost::any&, const std::vector<std::basic_string<charT> >&, T*, long int) [with T = mylist; charT = char]
/usr/include/boost/program_options/detail/value_semantic.hpp:129:10: note: void boost::program_options::validate(boost::any&, const std::vector<std::basic_string<charT> >&, std::vector<_RealType>*, int) [with T = int; charT = char]
You can use custom validator. Your code would be:
#include <vector>
#include <boost/program_options.hpp>
#include <iostream>
#include <vector>
using namespace std;
namespace po = boost::program_options;
class mylist : public vector<int> {
public:
};
void validate(boost::any& v,
const vector<string>& values,
mylist*, int) {
mylist dvalues;
for(vector<string>::const_iterator it = values.begin();
it != values.end();
++it) {
stringstream ss(*it);
copy(istream_iterator<int>(ss), istream_iterator<int>(),
back_inserter(dvalues));
if(!ss.eof()) {
throw ("Invalid coordinate specification");
}
}
v = mylist(dvalues);
}
int main (int argc, char* argv[])
{
//vector<int> ch_list; // This works
mylist ch_list; // This doesn't
po::options_description desc("Allowed options");
desc.add_options()
("ch", po::value< mylist >(&ch_list)->multitoken(), "Set channel numbers")
;
po::variables_map vm;
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
}
catch (po::error& e) {
cerr << "ERROR: " << e.what() << "\n";
return EXIT_FAILURE;
}
for (auto cc : ch_list)
cout << cc << endl;
return EXIT_SUCCESS;
}
Reference: boost::program_options config file option with multiple tokens