3

I am trying to play with boost::lambda, but I bumped on an error I cannot figure out how to solve.

I have the feeling this is a beginner error, so please excuse my ignorance (and, I have to admit, my laziness for not reading the whole boost lamda documentation too).

It seems that in some cases using boost::bind (or maybe boost::lambda::bind?), is better suited than boost::lambda, but I am not sure if it can be applied here. I would like not having to write a separate function for if cond(arg1) arg2.insert(arg1) ;, as it would defeat the purpose; it would not be much better than a functor I guess.

I am using boost 1.35 with VC9 at work. The errors are at the cond() and insert() calling sites: "C2664: cannot convert parameter 1 from 'boost::lambda::placeholder1_type"

I replicated the problem with this snippet with g++ on my cygwin.

#include <boost/function.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/if.hpp>

#include <boost/foreach.hpp>
#include <iostream>
#include <set>

void work(  boost::function<void(long)> doAction ) {
    long results[] = { 2, 5, 4 };
    BOOST_FOREACH( long r, results )
        doAction( r );
}

bool cond( long r ) { return r % 2 == 0 ; }

int main() {
    using namespace boost::lambda;
    std::set<long> myResults;
    work( 
        if_then( cond(_1) , boost::ref(myResults).get().insert(_1) ) );

    BOOST_FOREACH( long r, myResults )
        std::cout << r << "\n";
}

g++ errors:

lambda_test.cpp: In function ‘int main()’:
lambda_test.cpp:21:19: error: cannot convert ‘boost::lambda::placeholder1_type {aka const boost::lambda::lambda_functor<boost::lambda::placeholder<1> >}’ to ‘long int’ for argument ‘1’ to ‘bool cond(long int)’
if_then( cond(_1) , boost::ref(myResults).get().insert(_1) ) );
               ^
lambda_test.cpp:21:60: error: no matching function for call to ‘std::set<long int>::insert(boost::lambda::placeholder1_type&)’
if_then( cond(_1) , boost::ref(myResults).get().insert(_1) ) );

Any help would be appreciated,

Thanks

manlio
  • 18,345
  • 14
  • 76
  • 126
unagi
  • 428
  • 3
  • 15

1 Answers1

1

You're mixing deferred execution with immediate evaluation:

boost::ref(myResults).get().insert(_1)

Here, boost::ref(myResults) is not lazy, so .get() isn't either. The type of boost::ref(myResults).get() is just std::set<long> &, and that type's insert member function doesn't have an overload that takes a Boost Lambda placeholder.

I'm not well-versed in Boost Lambda (anymore) because I've moved to it's successor library, Boost Phoenix. Here's a 1-to-1 translation with fixes: Live On Coliru

#include <boost/phoenix.hpp>

#include <boost/foreach.hpp>
#include <iostream>
#include <set>

template <typename Action>
void work(  Action doAction ) {
    long results[] = { 2, 5, 4 };
    BOOST_FOREACH( long r, results )
        doAction( r );
}

bool cond( long r ) { return r % 2 == 0 ; }


int main() {
    namespace phx = boost::phoenix;
    using namespace phx::placeholders;

    std::set<long> myResults;
    work( 
        if_(phx::bind(cond, _1)) [ phx::insert(phx::ref(myResults), _1) ] );

    BOOST_FOREACH( long r, myResults )
        std::cout << r << "\n";
}

Prints

2
4

I'd suggest to look at Phoenix function adaptation, to avoid the bind expressions:

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for your promptness! Unfortunately I cannot update our boost version and boost::phoenix is not in 1.35 (there is a boost/spirit/phoenix.hpp however). May I ask you how I should fix the same code with boost 1.35 features? Another point, I do not understand my mixing of deferred execution with immediate evaluation. As there are placeholders how can it evaluate immediately? It is not related to [lambda variables delay](http://www.boost.org/doc/libs/1_35_0/doc/html/lambda/le_in_details.html#lambda.delaying_constants_and_variables) is it? – unagi May 12 '14 at 13:27
  • You can probably use the same code with `#include ` (optionally `phoenix_statement.hpp` as well). BOOST_PHOENIX_ADAPT_FUNCTION will not be available then – sehe May 12 '14 at 13:28
  • @unagi Regarding the "mixing up", I extended my explanation in the answer. I forgot about that before. – sehe May 12 '14 at 13:33
  • Oops, I made a mistake boost/spirit/phoenix is folder, no phoenix.hpp. I included all headers of this folder, but `‘phoenix’ is not a namespace-name`. Trying boost::spirit::phoenix was unsuccesful too. I am searching more, and will tell you if I can make it. Thanks – unagi May 12 '14 at 13:51
  • 1
    @unagi I think [this](http://coliru.stacked-crooked.com/a/7f5a76fa458c65b7) should work, but it is really awful, maybe sehe will know a way to make it more "palatable". (Adding the link makes it more useful :)). PS: [This](http://coliru.stacked-crooked.com/a/ecfe74d71f3e830f) is better. – llonesmiz May 12 '14 at 14:01
  • Oh my god! your first example is… scaring. The second looks more usable in real life but I am looking for a “self-contained” lambda solution. It would be a pity if it is not possible – unagi May 12 '14 at 14:18
  • I am going to accept this as an answer, as I guess the real solution is: update boost! cv_and_he also provided a nice snippet. Thank you to you two. – unagi May 24 '14 at 10:46