[spirit] Support for one-pass iterators?

Hi, I'm trying to use Boost.Spirit.Classic to parse a text file content (or, to be more generic, a content that is read from an STL stream). I created a grammar that would fit the content, but I'm not sure which iterator I should pass to the parse function. The parser does not guarantee that it will read the content only in one pass, and I'm sure my actors will want to read the content themselves as they are called. So, obviously, istream_iterator/istreambuf_iterator will not work. Is there a tool in Boost.Spirit that would provide content buffering during the parse? If not, what can be suggested to solve this problem? BTW, the content is line-aligned, i.e. each line is syntactically distinct from others. I was thinking about writing a loop of readline/parse combo but I'm not sure how efficient it would be with regard to the grammar definition construction/destruction within the parse function. Are these concerns valid? Thanks.

2009/10/18 Andrey Semashev <andrey.semashev@gmail.com>:
Is there a tool in Boost.Spirit that would provide content buffering during the parse? If not, what can be suggested to solve this problem?
http://www.boost.org/libs/spirit/classic/doc/multi_pass.html

2009/10/18 Andrey Semashev <andrey.semashev@gmail.com>:
Is there a tool in Boost.Spirit that would provide content buffering
during
the parse? If not, what can be suggested to solve this problem?
http://www.boost.org/libs/spirit/classic/doc/multi_pass.html
I'd suggest using the multi_pass implementation from the upcoming Spirit V2.1 (to be released with Boost V1.41). This implementation is faster, the iterators have a smaller footprint, and it is the version being maintained for the time being (docs are here: http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/support/mul ti_pass.html). Generally, I would like to encourage everybody thinking of starting a new project using Spirit to switch to the new version as it is a major step towards better usability, speed, functionality, etc. Regards Hartmut ------------------- Meet me at BoostCon http://boostcon.com

Hartmut Kaiser wrote:
2009/10/18 Andrey Semashev <andrey.semashev@gmail.com>:
Is there a tool in Boost.Spirit that would provide content buffering during the parse? If not, what can be suggested to solve this problem?
http://www.boost.org/libs/spirit/classic/doc/multi_pass.html
I'd suggest using the multi_pass implementation from the upcoming Spirit V2.1 (to be released with Boost V1.41). This implementation is faster, the iterators have a smaller footprint, and it is the version being maintained for the time being (docs are here: http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/support/mul ti_pass.html).
Generally, I would like to encourage everybody thinking of starting a new project using Spirit to switch to the new version as it is a major step towards better usability, speed, functionality, etc.
I'd be glad to take look at Spirit2, but AFAIK it is not documented. I'm not even sure it's stable. I'd really like to see a document listing the differences from 1.8 or a transition guide.

Generally, I would like to encourage everybody thinking of starting a new project using Spirit to switch to the new version as it is a major step towards better usability, speed, functionality, etc.
I'd be glad to take look at Spirit2, but AFAIK it is not documented.
It is: http://tinyurl.com/ojalum (and as I said this will be part of Boost V1.41).
I'm not even sure it's stable.
I'd really like to see a document listing the differences from 1.8 or a
Again, it is. Spirit V2.1 is the result of a 2 year beta phase of Spirit V2 (and it's a 100% rewrite of V2, resulting in faster compilation times, simpler design, and faster execution). I'm happy we finally manage to release it. transition guide. See here (not complete but a starting point): http://tinyurl.com/lk8r43. And, FWIW, any further questions will be happily answered on the Spirit list. Regards Hartmut ------------------- Meet me at BoostCon http://boostcon.com

Hartmut Kaiser wrote:
See here (not complete but a starting point): http://tinyurl.com/lk8r43. And, FWIW, any further questions will be happily answered on the Spirit list.
I did some reading and have one observation that I'd like to share without subscribing to the Spirit list (I have limited time to read this list already). So sorry if I'm being off-topic. The docs say that the grammar should pass a reference to the starting rule to the base class constructor (qi::grammar specialization). But at this point the rule's constructor has not yet been called. What's more, the base class calls a method on the passed reference. So, at least, the docs are not correct when it suggest implementing the grammar that way. The best way around this is using the base-from-member idiom, which, IMO, complicates the code quite a bit and is not mentioned in the docs, as far as I've seen. However, I would suggest to solve this by providing a way to post-initialize the base class, for example by calling some init method, like STL streams do.

Andrey Semashev wrote:
The docs say that the grammar should pass a reference to the starting rule to the base class constructor (qi::grammar specialization). But at this point the rule's constructor has not yet been called. What's more, the base class calls a method on the passed reference.
Where did you get that info? The base class does not call a method on the passed reference. That is not correct. It only does so at parse time and at that time, everything has already been constructed. Regards, -- Joel de Guzman http://www.boostpro.com http://spirit.sf.net http://www.facebook.com/djowel Meet me at BoostCon http://www.boostcon.com/home http://www.facebook.com/boostcon

Joel de Guzman wrote:
Andrey Semashev wrote:
The docs say that the grammar should pass a reference to the starting rule to the base class constructor (qi::grammar specialization). But at this point the rule's constructor has not yet been called. What's more, the base class calls a method on the passed reference.
Where did you get that info? The base class does not call a method on the passed reference. That is not correct. It only does so at parse time and at that time, everything has already been constructed.
Hmm... I may be misinterpreting what I see at boost/spirit/home/qi/nonterminal/grammar.hpp:54, in the release branch: grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(start.alias())) , name_(name_) {} Here "start" is a reference to a not yet constructed object.

Christian Schladetsch wrote:
grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(start.alias())) , name_(name_) {}
Here "start" is a reference to a not yet constructed object.
False.
Care to elaborate?

On Tue, Oct 20, 2009 at 2:46 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
Christian Schladetsch wrote:
grammar(
start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(start.alias())) , name_(name_) {}
Here "start" is a reference to a not yet constructed object.
False.
Care to elaborate?
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments.

Christian Schladetsch wrote:
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments.
That is not true. The base class constructor is called _before_ any members are constructed. Try it yourself.

On Tue, Oct 20, 2009 at 3:06 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
Christian Schladetsch wrote:
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments.
That is not true. The base class constructor is called _before_ any members are constructed. Try it yourself.
I am not sure what you are referring to. Before the C++ compiler passes a reference to something, it has constructed it. The example you gave was of a ctor taking a reference. Before that ctor is executed, I will personally guarantee that it and all it's base classes have been constructed. You are working under a delusion that should be corrected very soon. Regards, Christian

You can, of course, make a situation where you can pass a reference to a malformed instance. On Tue, Oct 20, 2009 at 3:11 AM, Christian Schladetsch < christian.schladetsch@gmail.com> wrote:
On Tue, Oct 20, 2009 at 3:06 AM, Andrey Semashev < andrey.semashev@gmail.com> wrote:
Christian Schladetsch wrote:
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments.
That is not true. The base class constructor is called _before_ any members are constructed. Try it yourself.
I am not sure what you are referring to. Before the C++ compiler passes a reference to something, it has constructed it. The example you gave was of a ctor taking a reference. Before that ctor is executed, I will personally guarantee that it and all it's base classes have been constructed.
You are working under a delusion that should be corrected very soon.
Regards, Christian

I'll bet that you are calling a virtual method from a base class's initialiser list, and blaming it on Boost.Spirit... On Tue, Oct 20, 2009 at 3:12 AM, Christian Schladetsch < christian.schladetsch@gmail.com> wrote:
You can, of course, make a situation where you can pass a reference to a malformed instance.
On Tue, Oct 20, 2009 at 3:11 AM, Christian Schladetsch < christian.schladetsch@gmail.com> wrote:
On Tue, Oct 20, 2009 at 3:06 AM, Andrey Semashev < andrey.semashev@gmail.com> wrote:
Christian Schladetsch wrote:
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments.
That is not true. The base class constructor is called _before_ any members are constructed. Try it yourself.
I am not sure what you are referring to. Before the C++ compiler passes a reference to something, it has constructed it. The example you gave was of a ctor taking a reference. Before that ctor is executed, I will personally guarantee that it and all it's base classes have been constructed.
You are working under a delusion that should be corrected very soon.
Regards, Christian

Christian Schladetsch wrote:
I'll bet that you are calling a virtual method from a base class's initialiser list, and blaming it on Boost.Spirit...
On Tue, Oct 20, 2009 at 3:12 AM, Christian Schladetsch < christian.schladetsch@gmail.com> wrote:
You can, of course, make a situation where you can pass a reference to a malformed instance.
Please, don't guess. I described the case in one of my previous postings: http://article.gmane.org/gmane.comp.lib.boost.devel/194839 The docs I am referring to are here, for example: http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/notes/porti... http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/qi/tutorial... and some other places.

Before I spend an hour researching the pathology of this ''bug": Am I right in assuming that the poster was complaining about Boost.Spirit using a malformed instance? An instance that was passed to it by reference? If so, I stand by all the facts I stated about said instance being created before being passed. If not, then there is more going on than I originally assumed, and I will henceforth bow out of the issue. But I remain interested in whether the caller can blame the callee in any case such as: void foo(X& x) { x.bar(); } Best Regards, Christian. On Tue, Oct 20, 2009 at 3:36 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
Christian Schladetsch wrote:
I'll bet that you are calling a virtual method from a base class's initialiser list, and blaming it on Boost.Spirit...
On Tue, Oct 20, 2009 at 3:12 AM, Christian Schladetsch < christian.schladetsch@gmail.com> wrote:
You can, of course, make a situation where you can pass a reference to a
malformed instance.
Please, don't guess. I described the case in one of my previous postings:
http://article.gmane.org/gmane.comp.lib.boost.devel/194839
The docs I am referring to are here, for example:
http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/notes/porti...
http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/qi/tutorial...
and some other places.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Christian Schladetsch wrote:
On Tue, Oct 20, 2009 at 3:36 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
Christian Schladetsch wrote:
I'll bet that you are calling a virtual method from a base class's initialiser list, and blaming it on Boost.Spirit...
On Tue, Oct 20, 2009 at 3:12 AM, Christian Schladetsch < christian.schladetsch@gmail.com> wrote:
You can, of course, make a situation where you can pass a reference to a
malformed instance.
Please, don't guess. I described the case in one of my previous postings:
http://article.gmane.org/gmane.comp.lib.boost.devel/194839
The docs I am referring to are here, for example:
http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/notes/porti...
http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/spirit/qi/tutorial...
and some other places.
Before I spend an hour researching the pathology of this ''bug":
Am I right in assuming that the poster was complaining about Boost.Spirit using a malformed instance? An instance that was passed to it by
reference?
If so, I stand by all the facts I stated about said instance being
created
before being passed.
If not, then there is more going on than I originally assumed, and I will henceforth bow out of the issue. But I remain interested in whether the caller can blame the callee in any case such as:
void foo(X& x) { x.bar(); }
If you read carefully the post I gave link to, the answer should be clear. The library docs suggest the usage pattern that is invalid with the current state of code. It is possible to make user's code compatible with the library implementation (by using base-from-member idiom), but (a) this requirement is not documented and (b) this complicates the code. Point (b) is especially significant for me as I'm considering to upgrade from Spirit 1.8 to 2.

grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make( start.alias())) , name_(name_) {} Here "start" is a reference to a not yet constructed object. No, it's not. "start" is a reference to a well-formed object - or at least to an object that is defined in user-space as well formed. My argument is based on this premise, and this alone: 'start' is fully created before it it used by the grammar. If this is not true, please speak to Stroustrup. If it is true, then I am right and I take no pleasure from it. I don't think you're unable to grasp what I am saying, and I am sure you think there is a problem. But I just don't see it. I don't care about the details: the fact is that if you are taking a reference then that object has been well formed or you are doing something silly. Regards, Christian

Christian Schladetsch wrote:
Here "start" is a reference to a not yet constructed object.
No, it's not. "start" is a reference to a well-formed object - or at least to an object that is defined in user-space as well formed.
My argument is based on this premise, and this alone: 'start' is fully created before it it used by the grammar.
If this is not true, please speak to Stroustrup. If it is true, then I am right and I take no pleasure from it. I don't think you're unable to grasp what I am saying, and I am sure you think there is a problem. But I just don't see it. I don't care about the details: the fact is that if you are taking a reference then that object has been well formed or you are doing something silly.
Christian, I understand your point and, abstracting away from the original case, I share it. However, I think your approach to the highlighted problem is not correct. You seem to ignore the fact that I'm speaking of a library that has a documented usage pattern and interface. I don't think that following that interface is "something silly", as you would call it. I propose to close our dispute at this point as it seems clear to me that we have different views on the discussed matter. What I'd really like to see is an answer from the library maintainer.

Andrey Semashev wrote:
If you read carefully the post I gave link to, the answer should be clear. The library docs suggest the usage pattern that is invalid with the current state of code. It is possible to make user's code compatible with the library implementation (by using base-from-member idiom), but (a) this requirement is not documented and (b) this complicates the code. Point (b) is especially significant for me as I'm considering to upgrade from Spirit 1.8 to 2.
Why don't you simply believe Hartmut when he tells you that everything is fine? OK, the dangerous looking code is as follows: grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make( start.alias())) , name_(name_) {} I completely agree that the constructor of start has not yet been called, because grammar is the base class of your object. However, the memory for start is already there, and the "this" pointer of start already has the "correct" value. Now, all "start.alias()" does is to return a wrapped reference to "*this" of the start object. Are you sure that this doesn't work? Regards, Thomas

Thomas Klimpel wrote:
Why don't you simply believe Hartmut when he tells you that everything is fine?
It's not fine, it's UB. Pure luck if it works on some (even most) platforms. I don't play dice.
OK, the dangerous looking code is as follows:
grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(
start.alias())) , name_(name_) {}
I completely agree that the constructor of start has not yet been called, because grammar is the base class of your object. However, the memory for start is already there, and the "this" pointer of start already has the "correct" value. Now, all "start.alias()" does is to return a wrapped reference to "*this" of the start object. Are you sure that this doesn't work?
Only compiler knows if would it actually work. I've seen enough strange compiler behaviors to reject such dangerous code. If the question is whether to switch from the conforming implementation to the non-conforming, my answer is "not to switch". Don't get me wrong, I'm not an enemy to Spirit2. I simply reported a problem with the new implementation interface. The developers may take it into account to improve the library, don't you think?

Christian Schladetsch wrote:
On Tue, Oct 20, 2009 at 3:06 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
Christian Schladetsch wrote:
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments.
That is not true. The base class constructor is called _before_ any members are constructed. Try it yourself.
I am not sure what you are referring to. Before the C++ compiler passes a reference to something, it has constructed it. The example you gave was of a ctor taking a reference. Before that ctor is executed, I will personally guarantee that it and all it's base classes have been constructed.
You can pass a reference to a data member to a base class constructor in a derivate's initializer list. If the base class constructor tries to use the reference, it will result in undefined behavior. (I'm not commenting on the supposed Spirit problem suggested by the OP.)
You are working under a delusion that should be corrected very soon.
I think you're jumping to conclusions. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Stewart, Robert wrote:
Christian Schladetsch wrote:
On Tue, Oct 20, 2009 at 3:06 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
Christian Schladetsch wrote:
Sure, but this is basic C++. Before the ctor to grammar(...) is invoked, the arguments to it have been evaluated. This requires construction of its arguments. That is not true. The base class constructor is called _before_ any members are constructed. Try it yourself.
I am not sure what you are referring to. Before the C++ compiler passes a reference to something, it has constructed it. The example you gave was of a ctor taking a reference. Before that ctor is executed, I will personally guarantee that it and all it's base classes have been constructed.
You can pass a reference to a data member to a base class constructor in a derivate's initializer list. If the base class constructor tries to use the reference, it will result in undefined behavior. (I'm not commenting on the supposed Spirit problem suggested by the OP.)
That is exactly the case suggested by the Spirit2 docs.

The docs say that the grammar should pass a reference to the starting rule to the base class constructor (qi::grammar specialization). But at this point the rule's constructor has not yet been called. What's more, the base class calls a method on the passed reference.
Where did you get that info? The base class does not call a method on the passed reference. That is not correct. It only does so at parse time and at that time, everything has already been constructed.
Hmm... I may be misinterpreting what I see at boost/spirit/home/qi/nonterminal/grammar.hpp:54, in the release branch:
grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(start.alias())) , name_(name_) {}
Here "start" is a reference to a not yet constructed object.
All the construct is doing is to use the address of start (which is known and well defined at this point). The address of an object doesn't change during construction. Just look at the code, you'll see it. The code does not refer to any member data items of 'start'. I see no problem with this. Do you have a real problem while using Spirit or is this discussion merely 'academic'? Regards Hartmut ------------------- Meet me at BoostCon http://boostcon.com

Hartmut Kaiser wrote:
Do you have a real problem while using Spirit or is this discussion merely 'academic'?
As I'm not using Spirit2 yet, no real problem. As I said, there is a user-side workaround, so even if I end up using Spirit2 I will arrange my code accordingly. Just thought it would be useful at least to document this twist or, at most, to provide a conforming initialization way.

Hartmut Kaiser wrote:
Hmm... I may be misinterpreting what I see at boost/spirit/home/qi/nonterminal/grammar.hpp:54, in the release branch:
grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(start.alias())) , name_(name_) {}
Here "start" is a reference to a not yet constructed object.
All the construct is doing is to use the address of start (which is known and well defined at this point). The address of an object doesn't change during construction. Just look at the code, you'll see it. The code does not refer to any member data items of 'start'. I see no problem with this.
It would appear that the point of confusion is "start.alias()." That appears to invoke a start_type member function on, apparently, a reference to a data member in a derivate. It is legal to use &start to initialize a pointer, but I think calling any member function is not. I'd have to look over the standard language carefully to state that unequivocally. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

The docs say that the grammar should pass a reference to the starting rule to the base class constructor (qi::grammar specialization). But at this point the rule's constructor has not yet been called. What's more, the base class calls a method on the passed reference.
Where did you get that info? The base class does not call a method on the passed reference. That is not correct. It only does so at parse time and at that time, everything has already been constructed.
Hmm... I may be misinterpreting what I see at boost/spirit/home/qi/nonterminal/grammar.hpp:54, in the release branch:
grammar( start_type const& start , std::string const& name_ = "unnamed-grammar") => : proto::extends<terminal, base_type>(terminal::make(start.alias())) , name_(name_) {}
Here "start" is a reference to a not yet constructed object.
All the construct is doing is to use the address of start (which is known and well defined at this point). The address of an object doesn't change during construction. Just look at the code, you'll see it. The code does not refer to any member data items of 'start'. I see no problem with this.
Do you have a real problem while using Spirit or is this discussion merely 'academic'?
I looked into the Standard and it says there [3.8.6]: <quote> Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any lvalue which refers to the original object may be used but only in limited ways. Such an lvalue refers to allocated storage (3.7.4.2), and using the properties of the lvalue which do not depend on its value is well-defined. </quote> Regards Hartmut ------------------- Meet me at BoostCon http://boostcon.com

Hartmut Kaiser wrote:
I looked into the Standard and it says there [3.8.6]:
<quote> Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an
object has ended and before the storage which the object occupied is reused or released, any lvalue which refers to the original object may be used but only in limited ways. Such an lvalue refers to allocated storage (3.7.4.2), and using the properties of the lvalue which do not depend on its value is well-defined. </quote>
Quoting a bit further: <quote> ... if the original object will be or was of a non-POD class type, the program has undefined behavior if: - the lvalue is used to access a non-static data member or call a non-static member function of the object, ... </quote>

I looked into the Standard and it says there [3.8.6]:
<quote> Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the
Hartmut Kaiser wrote: lifetime of an
object has ended and before the storage which the object occupied is
reused
or released, any lvalue which refers to the original object may be used but only in limited ways. Such an lvalue refers to allocated storage (3.7.4.2), and using the properties of the lvalue which do not depend on its value is well-defined. </quote>
Quoting a bit further:
<quote> ... if the original object will be or was of a non-POD class type, the program has undefined behavior if:
- the lvalue is used to access a non-static data member or call a non-static member function of the object, ... </quote>
Point taken, I changed the code not to invoke any member functions anymore. Thanks! Regards Hartmut ------------------- Meet me at BoostCon http://boostcon.com

Scott McMurray wrote:
2009/10/18 Andrey Semashev <andrey.semashev@gmail.com>:
Is there a tool in Boost.Spirit that would provide content buffering during the parse? If not, what can be suggested to solve this problem?
http://www.boost.org/libs/spirit/classic/doc/multi_pass.html
Thank you, I'll take a look.
participants (7)
-
Andrey Semashev
-
Christian Schladetsch
-
Hartmut Kaiser
-
Joel de Guzman
-
Scott McMurray
-
Stewart, Robert
-
Thomas Klimpel