How does these code works ?

B

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 T> T&(* is_reference_helper1(wrap<T>) )(wrap<T>);
char is_reference_helper1(...);

template <class T> no_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::type_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 !
 
A

Alf P. Steinbach

* 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 T> T&(* 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<T> argument, 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<T> argument 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<T> argument by
value, and returns a pointer to a function (the ResultFunc) that takes a
wrap<T> argument 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<T> returning a
pointer to a function taking a Type2Type<T> returning a T&", which
char is_reference_helper1(...);

template <class T> no_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::type_traits::wrap<T>())))
== 1
);
};

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
 
B

Bo Yang

Alf said:
* Bo Yang:

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<T> argument, 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<T> argument 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<T> argument by
value, and returns a pointer to a function (the ResultFunc) that takes a
wrap<T> argument 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<T> returning a
pointer to a function taking a Type2Type<T> returning 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 ?
 
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
 
B

Bo Yang

Alf said:
* Bo Yang:


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 T> no_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 .
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Bo Yang:


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 T> struct TypeCarrier {};

template< typename T> T* 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
 
B

Bo Yang

Alf said:
* Alf P. Steinbach:
* Bo Yang:


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 T> struct TypeCarrier {};

template< typename T> T* 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 T> void test( T& ) ;
 
A

Alf P. Steinbach

* 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 T> void 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
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top