how to use the PEGTL parser with a separate lexer? - c++

I have already a lexer and want to use my own token types as the input for a PEGTL parser. As an example, here is a modified version of the sum example, which reads from stdin:
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <tao/pegtl.hpp>
using namespace TAO_PEGTL_NAMESPACE;
namespace sum
{
struct num : seq< plus< digit > > {};
struct int_list
: list< num, one< ',' > >
{};
struct grammar
: seq< int_list, eof >
{};
template< typename Rule >
struct action
{};
template<>
struct action< num >
{
template< typename ActionInput >
static void apply( const ActionInput& in, int& sum )
{
sum += atoi(in.string().c_str());
}
};
} // namespace sum
struct Token {
typedef enum { COMMA, NUM, END_OF_FILE } Type;
Token(Type type, int num = 0) : type(type), num(num) {}
Type type;
int num;
};
int main()
{
// this works, can be called like this:
// echo -n "1,2,3" | ./a.out
int d = 0.0;
if( parse< sum::grammar, sum::action >( istream_input(std::cin, 16, "stdin"), d )) {
std::cout << "parsing OK; sum = " << d << std::endl;
}
else {
std::cout << "parsing failed" << std::endl;
}
// how can I do this when I have already the tokens in a vector?
std::vector<Token> tokens;
tokens.push_back(Token(Token::Type::NUM, 1));
tokens.push_back(Token(Token::Type::COMMA));
tokens.push_back(Token(Token::Type::NUM, 2));
tokens.push_back(Token(Token::Type::COMMA));
tokens.push_back(Token(Token::Type::NUM, 3));
tokens.push_back(Token(Token::Type::END_OF_FILE));
}
It can be compiled like this: g++ -std=c++17 -I PEGTL/include parser.cc. The files in PEGTL/include are from the PEGTL repository.
How do I need to modify the parser so that the vector of tokens are used instead of std::cin? I think I have to write my own ParseInput struct, but I couldn't define all required methods and types for it. And how can I use then the token objects in the parser for this sum example?

Related

Getting field names with boost::pfr

Hi I'm using boost::pfr for basic reflection, it works fine, but the problem is it is only print or deal with the field values, like with boost::pfr::io it prints each member of the struct, but how can I print it as name value pairs, same issue with for_each_field, the functor only accepts values, but not names. How can I get the field names?
struct S {
int n;
std::string name;
};
S o{1, "foo"};
std::cout << boost::pfr::io(o);
// Outputs: {1, "foo"}, how can I get n = 1, name = "foo"?
If you think adapting a struct is not too intrusive (it doesn't change your existing definitions, and you don't even need to have it in a public header):
BOOST_FUSION_ADAPT_STRUCT(S, n, name)
Then you can concoct a general operator<< for sequences:
namespace BF = boost::fusion;
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << BF::at_c<I>(v);
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return ((visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
(Prior to c++20 this would require some explicit template types instead of the lambdas, perhaps making it more readable. I guess I'm lazy...)
Here's a live demo: Live On Compiler Explorer
n = 1, name = foo
Bonus: Correctly quoting string-like types
Live On Compiler Explorer
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <iomanip>
namespace MyLib {
struct S {
int n;
std::string name;
};
namespace BF = boost::fusion;
static auto inline pretty(std::string_view sv) { return std::quoted(sv); }
template <typename T,
typename Enable = std::enable_if_t<
not std::is_constructible_v<std::string_view, T const&>>>
static inline T const& pretty(T const& v)
{
return v;
}
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << pretty(BF::at_c<I>(v));
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return (visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
} // namespace MyLib
BOOST_FUSION_ADAPT_STRUCT(MyLib::S, n, name)
int main()
{
MyLib::S o{1, "foo"};
std::cout << o << "\n";
}
Outputs:
n = 1, name = "foo"
The library cannot offer any such functionality because it is currently impossible to obtain the name of a member of a class as value of an object.
If you want to output field names, you need to declare string objects mapped with the members and implement a operator<< which uses these strings manually.
To do this a more sophisticated reflection library would probably offer macros to use in the definition of the members. Macros can expand their argument(s) into a declaration using the provided name as identifier while also producing code using the name as string literal (via the # macro replacement operator).
It's stupid but hey, with a stringifying macro per field it could be enough for you.
C++14, no additional library
#include <boost/pfr.hpp>
struct S
{
int n;
std::string name;
static char const* const s_memNames[2];
};
char const* const S::s_memNames[2] = {"n", "name"};
// utility
template< size_t I, typename TR >
char const* MemberName()
{
using T = std::remove_reference_t<TR>;
if (I < std::size(T::s_memNames))
return T::s_memNames[I];
return nullptr;
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
char const* n = MemberName<I,T>();
auto& v = boost::pfr::get<I>(inst);
cout << "(" << n << " = " << v << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output:
(n = 2)
(name = boo)
(previous version of the suggestion, this one has more fluff so less interesting)
#include <boost/pfr.hpp>
// library additions:
static char const* g_names[100];
template< size_t V >
struct Id : std::integral_constant<size_t, V > {};
template< size_t I, typename T >
using TypeAt = boost::pfr::tuple_element_t<I, T>;
template<std::size_t Pos, class Struct>
constexpr int Ni() // name index
{
return std::tuple_element_t<Pos, typename std::remove_reference_t<Struct>::NamesAt >::value;
}
struct StaticCaller
{
template< typename Functor >
StaticCaller(Functor f) { f();}
};
///
/// YOUR CODE HERE
struct S
{
using NamesAt = std::tuple<Id<__COUNTER__>, Id<__COUNTER__>>; // add this
int n;
std::string name;
static void Init() // add this
{
g_names[Ni<0,S>()] = "n";
g_names[Ni<1,S>()] = "name";
}
};
StaticCaller g_sc__LINE__(S::Init); // add this
// utilities
template< size_t I, typename T >
auto GetValueName(T&& inst)
{
return std::make_pair(boost::pfr::get<I>(inst), g_names[Ni<I,T>()]);
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
auto const& [v,n] = GetValueName<I>(inst);
cout << "(" << v << ", " << n << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output
(2, n)
(boo, name)

How to display as many white spaces as characters in a symbol's mapped value in front of it

I honestly give up (like many other before me) to find by myself the syntax of this still pretty simple generator of the boost-spirit-karma Library. I would like to display, before the string, as many white spaces as characters in the string:
typedef enum {A, B, C} E;
class EName : public ka::symbols<E, std::string>
{
public:
EName() {add (A,"A") (B,"the B") (C,"a C");}
};
class grm: public ka::grammar<iterator, E()>
{
public:
grm():grm::base_type(start)
{
namespace phx = boost::phoenix;
namespace ka = boost::spirit::karma;
start = ka::duplicate[ka::repeat(phx::bind(&std::string::size,b))[ka::lit(' ')] << b];
}
private:
ka::rule<iterator,E()> start;
EName b;
};
int main(int argc, char * argv[])
{
grm g;
E e = A;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,e);
std::cout << generated << "\n";
generated.clear();
e = B;
ka::generate(sink,g,e);
std::cout << generated << "\n";
return 0;
}
The expected output is thus one white space followed by "A" and on the next line 5 white spaces followed by "the B" (as "the B" is a 5 characters string).
I understand that maybe the variable "b" is not accessible in the context of the argument of the ka::repeat()[] generator...I tried ka::_val instead without success. I actually don't have enough experience with both karma, phoenix and fusion to build a path to the answer, although I probably have access to all needed information in the documentation. Hence I would also appreciate a few hint on how I could come to the answer just by the documentation (or by deduction) rather than by experience.
UPDATE:
I tried using a attribute cast without success:
namespace boost {
namespace spirit {
namespace traits {
template <>
struct transform_attribute<const E, std::string, ka::domain>
{
typedef std::string type;
static type pre(const E & e) {
EName s;
int num = s.find(e)->size();
return std::string(num, ' ');
}
};
} } }
followed by:
start = ka::attr_cast<std::string>(ka::string) << b;
But neither does it compile.
Your problem can be divided in two:
Get a string from the enum value.
Prepend as many spaces as characters there are in that string.
The second part of the problem is quite straightforward using the right_align directive. You can simply use:
prepend_spaces = ka::right_align(2*phx::size(ka::_val))[ka::string];
(If you wanted to use something else besides simply whitespace you could use the second parameter to right_align (for example ka::right_align(2*phx::size(ka::_val),ka::lit('*'))[ka::string]))
For the first part, you could do something with attr_cast as you have shown. In the code below I have used phx::bind in order to obtain the string from the symbol table.
Running on WandBox
#include <iostream>
#include <string>
#include <vector>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace phx = boost::phoenix;
namespace ka = boost::spirit::karma;
typedef enum {A, B, C} E;
class EName : public ka::symbols<E, std::string>
{
public:
EName() {add (A,"A") (B,"the B") (C,"a C");}
};
template <typename Iterator>
class grm: public ka::grammar<Iterator, E()>
{
public:
grm():grm::base_type(start)
{
prepend_spaces = ka::right_align(2*phx::size(ka::_val))[ka::string];
start = prepend_spaces[ka::_1=phx::bind(&EName::at<E>,phx::ref(name),ka::_val)];
}
private:
ka::rule<Iterator,E()> start;
ka::rule<Iterator,std::string()> prepend_spaces;
EName name;
};
int main(int argc, char * argv[])
{
grm<std::back_insert_iterator<std::string> > g;
E e = A;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,e);
std::cout << generated << "\n";
generated.clear();
e = B;
ka::generate(sink,g,e);
std::cout << generated << "\n";
generated.clear();
std::vector<E> v {A,B,C};
ka::generate(sink,+g,v);
std::cout << generated << "\n";
return 0;
}
I was not that far, so I post here my first working attempt. Other solutions are welcome as well.
namespace ka = boost::spirit::karma;
typedef enum {A, B, C} E;
class EName : public ka::symbols<E, std::string>
{
public:
EName() {add (A,"A") (B,"the B") (C,"a C");}
};
namespace boost {
namespace spirit {
namespace traits {
template <>
struct transform_attribute<const E, std::string, ka::domain>
{
typedef std::string type;
static type pre(const E & e) {
EName s;
int num = s.find(e)->size();
return std::string(num, ' ');
}
};
} } }
class grm: public ka::grammar<iterator, E()>
{
public:
grm():grm::base_type(start)
{
start = ka::duplicate[ka::attr_cast<std::string>(ka::string) << b];
}
private:
ka::rule<iterator,E()> start;
EName b;
};
int main(int argc, char * argv[])
{
grm g;
E e = A;
std::string generated;
std::back_insert_iterator<std::string> sink(generated);
ka::generate(sink,g,e);
std::cout << generated << "\n";
generated.clear();
e = B;
ka::generate(sink,g,e);
std::cout << generated << "\n";
return 0;
}

Is it possible to ASSERT_DOES_NOT_COMPILE with GTest?

Assume a template class where we assert at compile time that the integer template argument must be greater zero:
template<int N>
class A
{
public:
A() {
static_assert(N > 0, "N needs to be greater 0.");
}
};
Is it possible to create a googletest unit test that compiles, but reports the error at runtime? For example:
TEST(TestA, ConstructionNotAllowedWithZero)
{
ASSERT_DOES_NOT_COMPILE(
{
A< 0 > a;
}
);
}
There is a way, but sadly it's probably not the way you want.
My first thought was to try to get SFINAE to discount an overload by expanding an invalid lambda in an unevaluated context. Unhappily (in your case), this is specifically disallowed...
#define CODE { \
utter garbage \
}
struct test
{
template<class T>
static std::false_type try_compile(...) { return{}; }
template<class T>
static auto try_compile(int)
-> decltype([]() CODE, void(), std::true_type());
{ return {}; }
};
struct tag {};
using does_compile = decltype(test::try_compile<tag>(0));
output:
./maybe_compile.cpp:88:17: error: lambda expression in an unevaluated operand
-> decltype([]() CODE, void(), std::true_type());
So it was back to the drawing board and a good old system call to call out to the compiler...
#include <iostream>
#include <string>
#include <cstdlib>
#include <fstream>
#include <sstream>
struct temp_file {
temp_file()
: filename(std::tmpnam(nullptr))
{}
~temp_file() {
std::remove(filename.c_str());
}
std::string filename;
};
bool compiles(const std::string code, std::ostream& reasons)
{
using namespace std::string_literals;
temp_file capture_file;
temp_file cpp_file;
std::ofstream of(cpp_file.filename);
std::copy(std::begin(code), std::end(code), std::ostream_iterator<char>(of));
of.flush();
of.close();
const auto cmd_line = "c++ -x c++ -o /dev/null "s + cpp_file.filename + " 2> " + capture_file.filename;
auto val = system(cmd_line.c_str());
std::ifstream ifs(capture_file.filename);
reasons << ifs.rdbuf();
ifs.close();
return val == 0;
}
auto main() -> int
{
std::stringstream reasons1;
const auto code1 =
R"code(
#include <iostream>
int main() {
return 0;
}
)code";
std::cout << "compiles: " << compiles(code1, reasons1) << std::endl;
std::stringstream reasons2;
const auto code2 =
R"code(
#include <iostream>
int main() {
FOO!!!!XC#£$%^&*()VBNMYGHH
return 0;
}
)code";
std::cout << "compiles: " << compiles(code2, reasons2) << std::endl;
std::cout << "\nAnd here's why...\n";
std::cout << reasons2.str() << std::endl;
return 0;
}
which in my case gives the following example output:
compiles: 1
compiles: 0
And here's why...
/var/tmp/tmp.3.2dADZ7:4:9: error: use of undeclared identifier 'FOO'
FOO!!!!XC#£$%^&*()VBNMYGHH
^
/var/tmp/tmp.3.2dADZ7:4:19: error: non-ASCII characters are not allowed outside of literals and identifiers
FOO!!!!XC#£$%^&*()VBNMYGHH
^
2 errors generated.
of course you can add all the necessary macros around the call to compiles() in order to GTESTify it. You will of course have to set command line options on the c-compiler invocation to set the correct paths and defines.

How to stop string concatenation in Spirit Qi 'repeat' parser?

I would like to split a string into parts:
input = "part1/part2/part3/also3"
and fill the structure that consist of three std::string with these parts.
struct strings
{
std::string a; // <- part1
std::string b; // <- part2
std::string c; // <- part3/also3
};
However my parser seems to merge the parts together and store it into the first std::string.
Here is the code on coliru
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapted.hpp>
namespace qi = ::boost::spirit::qi;
struct strings
{
std::string a;
std::string b;
std::string c;
};
BOOST_FUSION_ADAPT_STRUCT(strings,
(std::string, a) (std::string, b) (std::string, c))
template <typename It>
struct split_string_grammar: qi::grammar<It, strings ()>
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
split_string = repeat (parts-1) [part > '/'] > last_part;
part = +(~char_ ("/"));
last_part = +char_;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, strings ()> split_string;
qi::rule<It, std::string ()> part, last_part;
};
int main ()
{
std::string const input { "one/two/three/four" };
auto const last = input.end ();
auto first = input.begin ();
// split into 3 parts.
split_string_grammar<decltype (first)> split_string (3);
strings ss;
bool ok = qi::parse (first, last, split_string, ss);
std::cout << "Parsed: " << ok << "\n";
if (ok) {
std::cout << "a:" << ss.a << "\n";
std::cout << "b:" << ss.b << "\n";
std::cout << "c:" << ss.c << "\n";
}
}
The output is:
Parsed: 1
a:onetwo
b:three/four
c:
while I expected:
Parsed: 1
a:one
b:two
c:three/four
I'd like not to modify the grammar heavily and leave "repeat" statement in it, because the "real" grammar is much more complex of course and I will need to have it there. Just need to find the way to disable the concatenations. I tried
repeat (parts-1) [as_string[part] > '/']
but that does not compile.
The trouble here is specifically that qi::repeat is documented to expose a container of element-types.
Now, because the exposed attribute type of the rule (strings) is not a container-type, Spirit "knows" how to flatten the values.
Of course it's not what you wanted in this case, but usually this heuristic makes for really convenient accumulation of string values.
Fix 1: use a container attribute
You could witness the reverse fix by getting rid of the non-container (sequence) target attribute:
Live On Coliru
//#define BOOST_SPIRIT_DEBUG
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapted.hpp>
namespace qi = ::boost::spirit::qi;
using strings = std::vector<std::string>;
template <typename It>
struct split_string_grammar: qi::grammar<It, strings ()>
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
split_string = repeat (parts-1) [part > '/']
> last_part
;
part = +(~char_ ("/"))
;
last_part = +char_
;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, strings ()> split_string;
qi::rule<It, std::string ()> part, last_part;
};
int main ()
{
std::string const input { "one/two/three/four" };
auto const last = input.end ();
auto first = input.begin ();
// split into 3 parts.
split_string_grammar<decltype (first)> split_string (3);
strings ss;
bool ok = qi::parse (first, last, split_string, ss);
std::cout << "Parsed: " << ok << "\n";
if (ok) {
for(auto i = 0ul; i<ss.size(); ++i)
std::cout << static_cast<char>('a'+i) << ":" << ss[i] << "\n";
}
}
What you really wanted:
Of course you want to keep the struct/sequence adaptation (?); In this case that's really tricky because as soon as you use any kind of Kleene operator (*,%) or qi::repeat you'll have the attribute transformation rules as outlined above, ruining your mood.
Luckily, I just remembered I have a hacky solution based on the auto_ parser. Note the caveat in this older answer though:
Read empty values with boost::spirit
CAVEAT Specializing for std::string directly like this might not be the best idea (it might not always be appropriate and might interact badly with other parsers).
By default create_parser<std::string> is not defined, so you might decide this usage is good enough for your case:
Live On Coliru
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
struct strings {
std::string a;
std::string b;
std::string c;
};
namespace boost { namespace spirit { namespace traits {
template <> struct create_parser<std::string> {
typedef proto::result_of::deep_copy<
BOOST_TYPEOF(
qi::lexeme [+(qi::char_ - '/')] | qi::attr("(unspecified)")
)
>::type type;
static type call() {
return proto::deep_copy(
qi::lexeme [+(qi::char_ - '/')] | qi::attr("(unspecified)")
);
}
};
}}}
BOOST_FUSION_ADAPT_STRUCT(strings, (std::string, a)(std::string, b)(std::string, c))
template <typename Iterator>
struct google_parser : qi::grammar<Iterator, strings()> {
google_parser() : google_parser::base_type(entry, "contacts") {
using namespace qi;
entry =
skip('/') [auto_]
;
}
private:
qi::rule<Iterator, strings()> entry;
};
int main() {
using It = std::string::const_iterator;
google_parser<It> p;
std::string const input = "part1/part2/part3/also3";
It f = input.begin(), l = input.end();
strings ss;
bool ok = qi::parse(f, l, p >> *qi::char_, ss, ss.c);
if (ok)
{
std::cout << "a:" << ss.a << "\n";
std::cout << "b:" << ss.b << "\n";
std::cout << "c:" << ss.c << "\n";
}
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
}
Prints
a:part1
b:part2
c:part3/also3
Update/Bonus
In reponse to the OP's own answer I wanted to challenge myself to write it more generically indeed.
The main thing is to to write set_field_ in such a way that it doesn't know/assume more than required about the destination sequence type.
With a bit of Boost Fusion magic that became:
struct set_field_
{
template <typename Seq, typename Value>
void operator() (Seq& seq, Value const& src, unsigned idx) const {
fus::fold(seq, 0u, Visit<Value> { idx, src });
}
private:
template <typename Value>
struct Visit {
unsigned target_idx;
Value const& value;
template <typename B>
unsigned operator()(unsigned i, B& dest) const {
if (target_idx == i) {
boost::spirit::traits::assign_to(value, dest);
}
return i + 1;
}
};
};
It has the added flexibility of applying Spirit's attribute compatibility rules¹. So, you can use the same grammar with both the following types:
struct strings {
std::string a, b, c;
};
struct alternative {
std::vector<char> first;
std::string second;
std::string third;
};
To drive the point home, I made the adaptation of the second struct reverse the field order:
BOOST_FUSION_ADAPT_STRUCT(strings, a, b, c)
BOOST_FUSION_ADAPT_STRUCT(alternative, third, second, first) // REVERSE ORDER :)
Without further ado, the demo program:
Live On Coliru
#define BOOST_SPIRIT_USE_PHOENIX_V3
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/algorithm/iteration.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace fus = boost::fusion;
struct strings {
std::string a, b, c;
};
struct alternative {
std::vector<char> first;
std::string second;
std::string third;
};
BOOST_FUSION_ADAPT_STRUCT(strings, a, b, c)
BOOST_FUSION_ADAPT_STRUCT(alternative, third, second, first) // REVERSE ORDER :)
// output helpers for demo:
namespace {
inline std::ostream& operator<<(std::ostream& os, strings const& data) {
return os
<< "a:\"" << data.a << "\" "
<< "b:\"" << data.b << "\" "
<< "c:\"" << data.c << "\" ";
}
inline std::ostream& operator<<(std::ostream& os, alternative const& data) {
os << "first: vector<char> { \""; os.write(&data.first[0], data.first.size()); os << "\" } ";
os << "second: \"" << data.second << "\" ";
os << "third: \"" << data.third << "\" ";
return os;
}
}
struct set_field_
{
template <typename Seq, typename Value>
void operator() (Seq& seq, Value const& src, unsigned idx) const {
fus::fold(seq, 0u, Visit<Value> { idx, src });
}
private:
template <typename Value>
struct Visit {
unsigned target_idx;
Value const& value;
template <typename B>
unsigned operator()(unsigned i, B& dest) const {
if (target_idx == i) {
boost::spirit::traits::assign_to(value, dest);
}
return i + 1;
}
};
};
boost::phoenix::function<set_field_> const set_field = {};
template <typename It, typename Target>
struct split_string_grammar: qi::grammar<It, Target(), qi::locals<unsigned> >
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
using boost::phoenix::val;
_a_type _current; // custom placeholder
split_string =
eps [ _current = 0u ]
> repeat (parts-1)
[part [ set_field(_val, _1, _current++) ] > '/']
> last_part [ set_field(_val, _1, _current++) ];
part = +(~char_ ("/"));
last_part = +char_;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, Target(), qi::locals<unsigned> > split_string;
qi::rule<It, std::string()> part, last_part;
};
template <size_t N = 3, typename Target>
void run_test(Target target) {
using It = std::string::const_iterator;
std::string const input { "one/two/three/four" };
It first = input.begin(), last = input.end();
split_string_grammar<It, Target> split_string(N);
bool ok = qi::parse (first, last, split_string, target);
if (ok) {
std::cout << target << '\n';
} else {
std::cout << "Parse failed\n";
}
if (first != last)
std::cout << "Remaining input left unparsed: '" << std::string(first, last) << "'\n";
}
int main ()
{
run_test(strings {});
run_test(alternative {});
}
Output:
a:"one" b:"two" c:"three/four"
first: vector<char> { "three/four" } second: "two" third: "one"
¹ as with BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
Besides sehe's suggestions one more possible way is to use semantic actions (coliru):
struct set_field_
{
void operator() (strings& dst, std::string const& src, unsigned& idx) const
{
assert (idx < 3);
switch (idx++) {
case 0: dst.a = src; break;
case 1: dst.b = src; break;
case 2: dst.c = src; break;
}
}
};
boost::phoenix::function<set_field_> const set_field { set_field_ {} };
template <typename It>
struct split_string_grammar: qi::grammar<It, strings (), qi::locals<unsigned> >
{
split_string_grammar (int parts)
: split_string_grammar::base_type (split_string)
{
assert (parts > 0);
using namespace qi;
using boost::phoenix::val;
split_string = eps [ _a = val (0) ]
> repeat (parts-1) [part [ set_field (_val, _1, _a) ] > '/']
> last_part [ set_field (_val, _1, _a) ];
part = +(~char_ ("/"));
last_part = +char_;
BOOST_SPIRIT_DEBUG_NODES ((split_string) (part) (last_part))
}
private:
qi::rule<It, strings (), qi::locals<unsigned> > split_string;
qi::rule<It, std::string ()> part, last_part;
};

How to get the value out of a multi index

I am trying to do one multi map(4 variables) using boost- multi index. Can I know how to get the value of a particular index like
tmp_Container.get<name>().find("aaa")->firstVariable
EDIT
struct EnumStruct
{
int microsoftEnum;
std::string microsoftEnumString;
int localEnum;
std::string localEnumString;
}
typedef bmi::multi_index_container<
EnumStruct,
bmi::ordered_unique<bmi::tag<localEnum>,bmi::member <
EnumStruct,std::string,&EnumStruct::localEnumString > >
>
> boost_Enum_Container;
boost_Enum_Container enumStructContainer;
int main()
{
enumStructContainer.insert(EnumStruct(1,"MS_1",11,"LOC11"));
enumStructContainer.insert(EnumStruct(2,"MS_2",22,"LOC22"));
enumStructContainer.insert(EnumStruct(3,"MS_3",33,"LOC33"));
//enumStructContainer.get<0>().find(1);
//boost_Enum_Container::index<localEnum>::type&
localEnum_Index=enumStructContainer.get<localEnum>().find(22)->firstVariable;
boost_Enum_Container::iterator
return 0;
}
You need the project function. See http://www.boost.org/doc/libs/1_39_0/libs/multi_index/doc/tutorial/basics.html#projection and http://www.boost.org/doc/libs/1_46_0/libs/multi_index/doc/reference/multi_index_container.html#projection. Thus: tmp_Container.project<0>(tmp_Container.get<name>.find("aaa")).
You are using boost::multi_index::tag in a wrong way. To use it, you must provide a valid struct.
Also you are trying to search a number in a string index. Below code must work, I did not test it though.
struct LOCALENUM { };
struct LOCALENUMSTR { };
using namespace boost::multi_index;
typedef boost::multi_index_container<
EnumStruct,
indexed_by<
ordered_unique<tag<LOCALENUM>,member<EnumStruct,int,&EnumStruct::localEnum > >
>,
ordered_unique<tag<LOCALENUMSTR>,member<EnumStruct,std::string,&EnumStruct::localEnumString > >
>
>
> boost_Enum_Container;
typedef boost_Enum_Container::index<LOCALENUM>::type::iterator EnumIterator;
typedef boost_Enum_Container::index<LOCALENUMSTR>::type::iterator EnumStrIterator;
EnumIterator enumiter = enumStructContainer.get<LOCALENUM>().find(22);
EnumStruct & enum_struct = (*eniter);
// There you go :
std::cout << enum_struct.microsoftEnumString << "\n";
//Searching for LocalEnumString :
EnumStrIterator enunstriter = enumStructContainer.get<LOCALENUMSTR>().find("LOC22");
EnumStruct & enum_struct = (*enunstriter );
std::cout << enum_struct.microsoftEnumString << "\n";
As has been mentioned multiple times in your other recent questions, the ordered_unique index specifier has the same interface as std::map; so if you know how to use std::map (hint, hint) then this should be very straightforward:
#include <string>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
struct EnumStruct
{
EnumStruct(int const me, std::string const& mes, int const le, std::string const& les)
: microsoftEnum(me),
microsoftEnumString(mes),
localEnum(le),
localEnumString(les)
{ }
int microsoftEnum;
std::string microsoftEnumString;
int localEnum;
std::string localEnumString;
};
namespace bmi = boost::multi_index;
struct localEnum_t;
typedef bmi::multi_index_container<
EnumStruct,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<localEnum_t>,
bmi::member<EnumStruct, int, &EnumStruct::localEnum>
>
>
> boost_Enum_Container;
int main()
{
boost_Enum_Container enumStructContainer;
enumStructContainer.insert(EnumStruct(1, "MS_1", 11, "LOC11"));
enumStructContainer.insert(EnumStruct(2, "MS_2", 22, "LOC22"));
enumStructContainer.insert(EnumStruct(3, "MS_3", 33, "LOC33"));
boost_Enum_Container::index_iterator<localEnum_t>::type iter = enumStructContainer.get<localEnum_t>().find(22);
if (iter != enumStructContainer.get<localEnum_t>().end())
std::cout << "found, localEnumString == " << iter->localEnumString << std::endl;
else
std::cout << "didn't find anything" << std::endl;
}