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

How does these code works ?

P: n/a
Hi ,
I am reading some boost code now , and
I got confused by the following code in the
add_reference type trait .

template <class TT&(* is_reference_helper1(wrap<T>) )(wrap<T>);
char is_reference_helper1(...);

template <class Tno_type is_reference_helper2(T&(*)(wrap<T>));
yes_type is_reference_helper2(...);

template <typename T>
struct is_reference_impl
{
BOOST_STATIC_CONSTANT(
bool, value = sizeof(
::boost::detail::is_reference_helper2(

::boost::detail::is_reference_helper1(::boost::typ e_traits::wrap<T>())))
== 1
);
};
Why does function is_reference_helper1 define in such
a way , I can't understand what is it !

Could someone explain these code for me ?
Thank you very much !

Oct 16 '06 #1
Share this Question
Share on Google+
7 Replies


P: n/a
* Bo Yang:
Hi ,
I am reading some boost code now , and
I got confused by the following code in the
add_reference type trait .

template <class TT&(* is_reference_helper1(wrap<T>) )(wrap<T>);
Yes, that /is/ confusing.

The main problem is perhaps to determine the "innermost" thing declared,
but as the famous Norwegian TV-cook Ingrid Espelid used to say, "but we
have cheated and ... voilą!", and out of the oven she lifted a perfect
.... something ... ; in this case, out of the oven comes the name
is_reference_helper1 (am I mixing metaphors?).

Looking at both sides of that name, the "function call" argument list to
the right takes precedence, it's done first, so to speak (although
nothing is done, but you can read a declaration in much the same way as
it would have been executed as an expression, from inner to outer).

So, hypothetically doing that hypothetical function call, we find that
is_reference_helper1 must be a function taking a wrap<Targument, by value.

Now for the result type, you need to work your way outwards in the
declaration.

And the first thing encountered then is the * on the left.

As an expression that would mean a dereferencing of a pointer, and so in
a declaration it means the thing declared /is/ a pointer (otherwise it
couldn't be dereferenced). So the hypothetical function call yields a
pointer. And so is_reference_helper1 is a function returning a pointer.

But a pointer to what?

Going still farther outwards in the declaration, we have on the left
'T&' and on the right '(wrap<T>)'. Again, if this were an expression
the thing on the right would be a function call's argument list, and
taking precedence. So we now know that the pointer is a pointer to a
function, let's call it ResultFunc, that takes a wrap<Targument by value.

Finally, the result type of ResultFunc is 'T&', a reference to an object
of type T.

So is_reference_helper1 is a function that takes a wrap<Targument by
value, and returns a pointer to a function (the ResultFunc) that takes a
wrap<Targument by value and returns a reference to a T object.

Incidentally, Google code search, applied to 'is_reference_helper1', in
addition to finding that line in the Boost library, turned up this
description from the MSVC port of Andrei Alexandrescu's Loki Library:
"is_reference_helper1 is a function taking a Type2Type<Treturning a
pointer to a function taking a Type2Type<Treturning a T&", which
removes some of the mystery of what that 'wrap<T>' might be.

char is_reference_helper1(...);

template <class Tno_type is_reference_helper2(T&(*)(wrap<T>));
yes_type is_reference_helper2(...);

template <typename T>
struct is_reference_impl
{
BOOST_STATIC_CONSTANT(
bool, value = sizeof(
::boost::detail::is_reference_helper2(

::boost::detail::is_reference_helper1(::boost::typ e_traits::wrap<T>())))
== 1
);
};
Ah, well, I'll leave you to it! <g>

Why does function is_reference_helper1 define in such
a way , I can't understand what is it !
That's because Real Programmers can't be bothered with using 'typedef',
or generally, naming things. After all, if the code could be understood
by others, then one might soon be replaced by someone else. Okay,
that's unfair, especially to Andrei who (it seems) bothered to write the
clear explanatory comment; an equally probable explanation is that in
the heat of the hunt, make that thing work, one has to try out many
different things, and the less typing per thing tried the better, and
then the first thing that works becomes embedded in stone, even if
unreadable, because one is already moving on to the next problem...

Could someone explain these code for me ?
See above.

Thank you very much !
You're welcome.

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Oct 16 '06 #2

P: n/a

Alf P. Steinbach wrote:
* Bo Yang:
Hi ,
I am reading some boost code now , and
I got confused by the following code in the
add_reference type trait .

template <class TT&(* is_reference_helper1(wrap<T>) )(wrap<T>);

Yes, that /is/ confusing.

The main problem is perhaps to determine the "innermost" thing declared,
but as the famous Norwegian TV-cook Ingrid Espelid used to say, "but we
have cheated and ... voilą!", and out of the oven she lifted a perfect
... something ... ; in this case, out of the oven comes the name
is_reference_helper1 (am I mixing metaphors?).

Looking at both sides of that name, the "function call" argument list to
the right takes precedence, it's done first, so to speak (although
nothing is done, but you can read a declaration in much the same way as
it would have been executed as an expression, from inner to outer).

So, hypothetically doing that hypothetical function call, we find that
is_reference_helper1 must be a function taking a wrap<Targument, by value.

Now for the result type, you need to work your way outwards in the
declaration.

And the first thing encountered then is the * on the left.

As an expression that would mean a dereferencing of a pointer, and so in
a declaration it means the thing declared /is/ a pointer (otherwise it
couldn't be dereferenced). So the hypothetical function call yields a
pointer. And so is_reference_helper1 is a function returning a pointer.

But a pointer to what?

Going still farther outwards in the declaration, we have on the left
'T&' and on the right '(wrap<T>)'. Again, if this were an expression
the thing on the right would be a function call's argument list, and
taking precedence. So we now know that the pointer is a pointer to a
function, let's call it ResultFunc, that takes a wrap<Targument by value.

Finally, the result type of ResultFunc is 'T&', a reference to an object
of type T.

So is_reference_helper1 is a function that takes a wrap<Targument by
value, and returns a pointer to a function (the ResultFunc) that takes a
wrap<Targument by value and returns a reference to a T object.

Incidentally, Google code search, applied to 'is_reference_helper1', in
addition to finding that line in the Boost library, turned up this
description from the MSVC port of Andrei Alexandrescu's Loki Library:
"is_reference_helper1 is a function taking a Type2Type<Treturning a
pointer to a function taking a Type2Type<Treturning a T&", which
removes some of the mystery of what that 'wrap<T>' might be.

Thank you very much .
But , how does these code detect whether
the typename T is a reference or not ?
Although I understand what the code mean , but
I still can't grasp how it tell whether T is a reference ?

Oct 16 '06 #3

P: n/a
* Bo Yang:
But , how does these code detect whether
the typename T is a reference or not ?
Although I understand what the code mean , but
I still can't grasp how it tell whether T is a reference ?
It's a case of SFINAE (Substitution Failure Is Not An Error). Consider
that there are two declarations of is_reference_helper1. Since the
first one has result type T& it fails outright to be matched with T when
T is a reference type, because you can't (currently, as of 2006) form a
reference to a reference (SFINAE kicks in, we have failure, but not an
error). That selects the second declaration of is_reference_helper1,
which has a nice, always valid 'char' result type, but argument type
'...' which is the guaranteed worst match in an overload set, so that it
won't be selected except when SFINAE kicks in for the first one.

Because of the possible failure, and also because T might be any size,
the size of the result type of is_reference_helper1 cannot be used
directly to compute the boolean yes/no of whether T is a reference type.
But the type of the result can be used indirectly, namely to select
from another overload set a function whose result type provides a
distinguishable size. Hence the is_reference_helper2 overloads, with
argument types that match the two possible is_reference_helper1
functions, and easily distinguished result type sizes.

Which result type is plugged into sizeof to make it all happen at
compile time -- and of course, to determine the size, which determines
which is_reference_helper1 was selected, which determines whether T is a
reference type or not.
Hth. (it's a kind of rather ingenious TMP code, relying not only on
undefined functions but also on constructs that won't compile --
except when they're passed by via the SFINAE avoidance mechanism)

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Oct 16 '06 #4

P: n/a

Alf P. Steinbach wrote:
* Bo Yang:
But , how does these code detect whether
the typename T is a reference or not ?
Although I understand what the code mean , but
I still can't grasp how it tell whether T is a reference ?

It's a case of SFINAE (Substitution Failure Is Not An Error). Consider
that there are two declarations of is_reference_helper1. Since the
first one has result type T& it fails outright to be matched with T when
T is a reference type, because you can't (currently, as of 2006) form a
reference to a reference (SFINAE kicks in, we have failure, but not an
error). That selects the second declaration of is_reference_helper1,
which has a nice, always valid 'char' result type, but argument type
'...' which is the guaranteed worst match in an overload set, so that it
won't be selected except when SFINAE kicks in for the first one.

Because of the possible failure, and also because T might be any size,
the size of the result type of is_reference_helper1 cannot be used
directly to compute the boolean yes/no of whether T is a reference type.
But the type of the result can be used indirectly, namely to select
from another overload set a function whose result type provides a
distinguishable size. Hence the is_reference_helper2 overloads, with
argument types that match the two possible is_reference_helper1
functions, and easily distinguished result type sizes.

Which result type is plugged into sizeof to make it all happen at
compile time -- and of course, to determine the size, which determines
which is_reference_helper1 was selected, which determines whether T is a
reference type or not.
Thanks very much , I think I know these code fully .
It is the T& which cause the substitution failed and detect whether T
is a reference type .
So we can just use template

template <typename Tno_type is_reference_helper( T& ) ;
yes_type is_reference_helper(...);

to detect whether the T is a reference .
But the code doesn't , does this just because the code don't
want to construct the object T ?
>
Hth. (it's a kind of rather ingenious TMP code, relying not only on
undefined functions but also on constructs that won't compile --
except when they're passed by via the SFINAE avoidance mechanism)
I find C++ is so profound , I need more time to grasp the concept
of Generic Programming .
- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Oct 16 '06 #5

P: n/a
* Alf P. Steinbach:
* Bo Yang:
>But , how does these code detect whether
the typename T is a reference or not ?
Although I understand what the code mean , but
I still can't grasp how it tell whether T is a reference ?

It's a case of SFINAE (Substitution Failure Is Not An Error). Consider
that there are two declarations of is_reference_helper1. Since the
first one has result type T& it fails outright to be matched with T when
T is a reference type, because you can't (currently, as of 2006) form a
reference to a reference (SFINAE kicks in, we have failure, but not an
error). That selects the second declaration of is_reference_helper1,
which has a nice, always valid 'char' result type, but argument type
'...' which is the guaranteed worst match in an overload set, so that it
won't be selected except when SFINAE kicks in for the first one.

Because of the possible failure, and also because T might be any size,
the size of the result type of is_reference_helper1 cannot be used
directly to compute the boolean yes/no of whether T is a reference type.
But the type of the result can be used indirectly, namely to select
from another overload set a function whose result type provides a
distinguishable size. Hence the is_reference_helper2 overloads, with
argument types that match the two possible is_reference_helper1
functions, and easily distinguished result type sizes.

Which result type is plugged into sizeof to make it all happen at
compile time -- and of course, to determine the size, which determines
which is_reference_helper1 was selected, which determines whether T is a
reference type or not.
Hth. (it's a kind of rather ingenious TMP code, relying not only on
undefined functions but also on constructs that won't compile -- except
when they're passed by via the SFINAE avoidance mechanism)

- Alf
OTOH., I'm not sure I really understand the code (or rather, I'm
beginning to strongly suspect I don't really understand it), because the
following seemingly much simpler code, based on the principles explained
above, seems to do the job:

<code>
#include <iostream>
#include <ostream>
#include <cstddef>

std::size_t const largerThanPointer = 666*sizeof(void*);

template< typename Tstruct TypeCarrier {};

template< typename TT* isRefHelper( TypeCarrier<T);
char (&isRefHelper( ... ))[largerThanPointer];

template< typename T >
class IsRef
{
public:
enum{ yes = (sizeof( isRefHelper( TypeCarrier<T>() ) ) ==
largerThanPointer ) };
};

int main()
{
typedef int NotRef;
typedef int& Ref;

std::cout << "Not ref yields " << IsRef<NotRef>::yes << std::endl;
std::cout << "Ref yields " << IsRef<Ref>::yes << std::endl;
}
</code>
Perhaps it's time to yet again delve into Andrei's book "Modern C++
Design" and check out the rationale... ;-)

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Oct 16 '06 #6

P: n/a

Alf P. Steinbach wrote:
* Alf P. Steinbach:
* Bo Yang:
But , how does these code detect whether
the typename T is a reference or not ?
Although I understand what the code mean , but
I still can't grasp how it tell whether T is a reference ?
It's a case of SFINAE (Substitution Failure Is Not An Error). Consider
that there are two declarations of is_reference_helper1. Since the
first one has result type T& it fails outright to be matched with T when
T is a reference type, because you can't (currently, as of 2006) form a
reference to a reference (SFINAE kicks in, we have failure, but not an
error). That selects the second declaration of is_reference_helper1,
which has a nice, always valid 'char' result type, but argument type
'...' which is the guaranteed worst match in an overload set, so that it
won't be selected except when SFINAE kicks in for the first one.

Because of the possible failure, and also because T might be any size,
the size of the result type of is_reference_helper1 cannot be used
directly to compute the boolean yes/no of whether T is a reference type.
But the type of the result can be used indirectly, namely to select
from another overload set a function whose result type provides a
distinguishable size. Hence the is_reference_helper2 overloads, with
argument types that match the two possible is_reference_helper1
functions, and easily distinguished result type sizes.

Which result type is plugged into sizeof to make it all happen at
compile time -- and of course, to determine the size, which determines
which is_reference_helper1 was selected, which determines whether T is a
reference type or not.
Hth. (it's a kind of rather ingenious TMP code, relying not only on
undefined functions but also on constructs that won't compile -- except
when they're passed by via the SFINAE avoidance mechanism)

- Alf

OTOH., I'm not sure I really understand the code (or rather, I'm
beginning to strongly suspect I don't really understand it), because the
following seemingly much simpler code, based on the principles explained
above, seems to do the job:

<code>
#include <iostream>
#include <ostream>
#include <cstddef>

std::size_t const largerThanPointer = 666*sizeof(void*);

template< typename Tstruct TypeCarrier {};

template< typename TT* isRefHelper( TypeCarrier<T);
char (&isRefHelper( ... ))[largerThanPointer];
I still can't understand why there must to be the return type
to test the type , why to use a function's argument to test the
type . just like

template <typename Tvoid test( T& ) ;

Oct 16 '06 #7

P: n/a
* Bo Yang:
>
I still can't understand why there must to be the return type
to test the type , why to use a function's argument to test the
type . just like

template <typename Tvoid test( T& ) ;
Sorry, I don't understand the question.

But a general solution to understanding something in programming is:
just try to implement it.

So that's what I advice.
Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Oct 16 '06 #8

This discussion thread is closed

Replies have been disabled for this discussion.