boost::lambda

F

flopbucket

Hi,

After reading a bit about boost::lambda, I became curious how they
implemented it. I downloaded it and had a look, but the all the
headers and multiple templates make it a bit difficult to follow in a
short time (I only spent maybe 15 minutes).

Anyway, I decided to try some ideas out and came up with the following
basic example: (obviously this is very far from boost::lambda and is
special for streams, etc., but just trying to understand the basic
techniques they use).

template<class T>
class Placeholder
{
public:
Placeholder() { }

void operator()(std::string x)
{
*stream << x;
}

std::eek:stream *stream;
};


Placeholder<std::string> X;

template<class T>
Placeholder<T>& operator<<(std::eek:stream& os, Placeholder<T>& t) {
t.stream = &os;
return t;
}


int main(int argc, char **argv)
{
(std::cout << X)("test"); // outputs "test"
}


Is this the *basic* idea of how they accomplish this?

One question I have that I couldnt figure out is, since the placeholder
needs to store a value, how can this be done without knowing before
hand? In my example, I delcared X to be Placeholder<std::string> since
I am using a string in my simple test, but I know with boost::lambda, I
could put other types and _1 just works. I guess it is some template
magic that I can't figure out right now, but any hints / tips
appreciated.


Thanks in advance.
 
S

Steve Hicks

flopbucket said:
After reading a bit about boost::lambda, I became curious how they
implemented it. I downloaded it and had a look, but the all the
headers and multiple templates make it a bit difficult to follow in a
short time (I only spent maybe 15 minutes).

Anyway, I decided to try some ideas out and came up with the following
basic example: (obviously this is very far from boost::lambda and is
special for streams, etc., but just trying to understand the basic
techniques they use).

(snip code)

Is this the *basic* idea of how they accomplish this?

One question I have that I couldnt figure out is, since the placeholder
needs to store a value, how can this be done without knowing before
hand? In my example, I delcared X to be Placeholder<std::string> since
I am using a string in my simple test, but I know with boost::lambda, I
could put other types and _1 just works. I guess it is some template
magic that I can't figure out right now, but any hints / tips
appreciated.

Here is my attempt. This is the smallest example I could throw
together that (a) compiles, and (b) does something interesting.

namespace {
// This is basically the placeholder
class identity_functor {
public:
// Return type
template<typename arg1_type>
struct return_type {
typedef arg1_type type;
};
// Constructor (use default)
// Operator
template<typename arg1_type>
typename return_type<arg1_type>::type operator()(arg1_type s) {
return s;
}
};

// Constant functor
template<typename T>
class const_functor {
T _data;
public:
// Return type
template<typename arg1_type>
struct return_type {
typedef T type;
};
// Constructor (implicit)
const_functor(T __data)
: _data(__data) { }
// Operator
template<typename arg1_type>
typename return_type<arg1_type>::type operator()(arg1_type) {
return _data;
}
};

// Helper function (I shouldn't need this)
template <typename T>
const_functor<T> constant(T t) {
return const_functor<T>(t);
}

// Sum functor
template <typename left_type,typename right_type>
class sum_functor {
left_type _left;
right_type _right;
public:
// Return type
template <typename arg1_type>
struct return_type {
typedef typename left_type::template
return_type<arg1_type>::type type;
};
// Constructor
sum_functor(left_type __left,right_type __right)
: _left(__left),
_right(__right) { }
// Operator
template<typename arg1_type>
typename return_type<arg1_type>::type operator()(arg1_type s) {
return _left(s)+_right(s);
}
};

// + operator
template<typename left_type,typename right_type>
sum_functor<left_type,right_type> operator+(left_type
_left,right_type _right) {
return sum_functor<left_type,right_type>(_left,_right);
}

// Placeholders
identity_functor X;
}

int main() {
std::cout << (X+constant(1))(5) << std::endl;
return 0;
}

It appears to me that this is basically how boost::lambda works. The
trickiest part seems to be determining the return types. I just chose
to use the left-hand argument of addition to determine the type,
although it would be better to make a two-argument template which
picked the more precise type of the two. We just pass around arg1_type
as a template argument which is the type of the placeholder.

Several caveats: First, I had to put it into an unnamed namespace
because I use global templates to overload the operator. I wish I
could have a lambda_functor base class, but we can't have the templated
operator() be virtual (and we probably don't want to use inheritance
anyway, since it would slow things down). One downside to this is the
explicit constant() call which is required so that we operate on two
functor objects.

Second, there's a lot of issues with type traits. The reason I chose
operator+ as an example is because streams require references, while
these are all values. To get any more complicated, we'd need to have
to get into casting the template types into references and consts, and
I really don't understand that at all.

If anyone has further comments, I would really appreciate your input.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,588
Members
45,093
Latest member
Vinaykumarnevatia00

Latest Threads

Top