 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Bo Yang Guest
|
Posted: Mon Oct 16, 2006 7:45 am Post subject: How does these code works ? |
|
|
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 ! |
|
| Back to top |
|
 |
Alf P. Steinbach Guest
|
Posted: Mon Oct 16, 2006 8:55 am Post subject: Re: How does these code works ? |
|
|
* Bo Yang:
| Quote: | 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
removes some of the mystery of what that 'wrap<T>' might be.
| Quote: | 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
);
};
|
Ah, well, I'll leave you to it! <g>
| Quote: | 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...
| Quote: | Could someone explain these code for me ?
|
See above.
| Quote: | 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? |
|
| Back to top |
|
 |
Alf P. Steinbach Guest
|
Posted: Mon Oct 16, 2006 9:10 am Post subject: Re: How does these code works ? |
|
|
* Alf P. Steinbach:
| Quote: | * 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 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
--
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? |
|
| Back to top |
|
 |
Bo Yang Guest
|
Posted: Mon Oct 16, 2006 9:10 am Post subject: Re: How does these code works ? |
|
|
Alf P. Steinbach wrote:
| Quote: | * 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 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 ?
| Quote: |
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 .
| Quote: | - 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? |
|
|
| Back to top |
|
 |
Alf P. Steinbach Guest
|
Posted: Mon Oct 16, 2006 9:10 am Post subject: Re: How does these code works ? |
|
|
* Bo Yang:
| Quote: | 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? |
|
| Back to top |
|
 |
Bo Yang Guest
|
Posted: Mon Oct 16, 2006 9:10 am Post subject: Re: How does these code works ? |
|
|
Alf P. Steinbach wrote:
| Quote: | * 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
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 ? |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|