By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,388 Members | 1,836 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,388 IT Pros & Developers. It's quick & easy.

boost::lambda

P: n/a
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::ostream *stream;
};
Placeholder<std::string> X;

template<class T>
Placeholder<T>& operator<<(std::ostream& 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.

Jun 28 '06 #1
Share this Question
Share on Google+
1 Reply


P: n/a
flopbucket wrote:
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::stringsince
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<Tconstant(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_typeoperator+(left_typ e
_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.
--
Steve Hicks

Jul 3 '06 #2

This discussion thread is closed

Replies have been disabled for this discussion.