You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@stdcxx.apache.org by Martin Sebor <se...@roguewave.com> on 2008/06/27 01:45:11 UTC

implementation of Unary Traits

The implementation of Unary Traits (e.g., is_void) uses explicit
specialization on all four combinations of cv-qualifiers for each
trait (plain, const, volatile, and const volatile). I'm wondering
if the alternative approach of stripping the qualifiers before
"dispatching" to just one explicit specialization has been
considered. The potential advantage of this approach is fewer
declarations, smaller translation units, and thus (presumably)
faster compilation.

Martin

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
>  
> 
> Martin Sebor wrote:
>> Travis Vitek wrote:
>>>  
>>>
> 
> [...]
> 
>> I can think of a couple of options that satisfy these. I'm not sure
>> how palatable I find them yet, but I might get over their (initial)
>> lack of appeal if the payoff is worth it. YMMV.
>>
>> One is to drop the cv specializations of the __rw_is_xxx traits and
>> define the standard traits in terms of both remove_cv and the __rw
>> traits, like so:
>>
>>   template <class _TypeT>
>>   struct is_void:
>>       integral_constant<bool,
>>           _RW::__rw_is_void<typename
>>               _RW::__rw_remove_cv<_TypeT>::type>::value>
>>   { };
>>
>> or more concisely:
>>
>>   template <class _TypeT>
>>   struct is_void:
>>       integral_constant<bool, _RWSTD_IS_VOID (_TypeT)::value>
>>   { };
>>
>> with _RWSTD_IS_VOID() being #defined like so:
>>
>>   #define _RWSTD_IS_VOID(T) \
>>       _RW::__rw_is_void<typename _RW::__rw_remove_cv<T>::type>
>>
>> All internal code would always need to use _RWSTD_IS_VOID() and
>> never _RW::__rw_is_void directly.
>>
> 
> I think there is a problem with _RWSTD_IS_VOID() being defined like this
> because it can't be used outside a template (because of the typename
> keyword being used in the macro definition).

I hadn't considered using the Unary Traits in non-template code.
I don't think it's likely but it certainly seems plausible (e.g.,
to insulate the implementation of an algorithm from changes to
some of the fundamental properties of an implementation-defined
class type such as whether it has a trivial default ctor, etc.)

That said, I'm not sure that this use case is one that we need
to concern ourselves with. To minimize footprint and insulate
user code from implementation details we make an effort to
confine as much non-generic code as possible into .cpp files
where using the public (std::) traits is fine and preferable
over relying on implementation details (i.e., all the __rw_xxx
traits that we use in library headers to reduce namespace
pollution).

Martin

> 
>   $ cat u.cpp && g++ u.cpp
>   namespace __rw {
> 
> 
>   template <class T>
>   struct __rw_remove_cv { typedef T type; };
> 
>   template <class T>
>   struct __rw_is_void { enum { value = 0 }; };
> 
>   template <>
>   struct __rw_is_void<void> { enum { value = 1 }; };
> 
> 
>   }
> 
>   #define _RW __rw
> 
>   #define _RWSTD_IS_VOID(T) \
>       _RW::__rw_is_void<typename _RW::__rw_remove_cv<T>::type>
> 
>   int main ()
>   {
>       const bool b = _RWSTD_IS_VOID(void)::value;
>       return b == 0;
>   }
>   u.cpp: In function 'int main()':
>   u.cpp:23: error: using 'typename' outside of template
> 
> 
> Travis
> 


RE: implementation of Unary Traits

Posted by Travis Vitek <Tr...@roguewave.com>.
 

Martin Sebor wrote:
>Travis Vitek wrote:
>>  
>> 

[...]

>
>I can think of a couple of options that satisfy these. I'm not sure
>how palatable I find them yet, but I might get over their (initial)
>lack of appeal if the payoff is worth it. YMMV.
>
>One is to drop the cv specializations of the __rw_is_xxx traits and
>define the standard traits in terms of both remove_cv and the __rw
>traits, like so:
>
>   template <class _TypeT>
>   struct is_void:
>       integral_constant<bool,
>           _RW::__rw_is_void<typename
>               _RW::__rw_remove_cv<_TypeT>::type>::value>
>   { };
>
>or more concisely:
>
>   template <class _TypeT>
>   struct is_void:
>       integral_constant<bool, _RWSTD_IS_VOID (_TypeT)::value>
>   { };
>
>with _RWSTD_IS_VOID() being #defined like so:
>
>   #define _RWSTD_IS_VOID(T) \
>       _RW::__rw_is_void<typename _RW::__rw_remove_cv<T>::type>
>
>All internal code would always need to use _RWSTD_IS_VOID() and
>never _RW::__rw_is_void directly.
>

I think there is a problem with _RWSTD_IS_VOID() being defined like this
because it can't be used outside a template (because of the typename
keyword being used in the macro definition).

  $ cat u.cpp && g++ u.cpp
  namespace __rw {


  template <class T>
  struct __rw_remove_cv { typedef T type; };

  template <class T>
  struct __rw_is_void { enum { value = 0 }; };

  template <>
  struct __rw_is_void<void> { enum { value = 1 }; };


  }

  #define _RW __rw

  #define _RWSTD_IS_VOID(T) \
      _RW::__rw_is_void<typename _RW::__rw_remove_cv<T>::type>

  int main ()
  {
      const bool b = _RWSTD_IS_VOID(void)::value;
      return b == 0;
  }
  u.cpp: In function 'int main()':
  u.cpp:23: error: using 'typename' outside of template


Travis

>

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
>  
[...]
> Here is one alternative that we might want to consider. Instead of
> stripping cv qualifiers, we add them and then provide a specialization
> for the cv-qualified type.
> 
>   template <class T>
>   struct is_cv_void { typedef false_type type; };
> 
>   template <> struct
>   struct is_cv_void<const volatile void> { typedef true_type type; };
> 
>   template <class T>
>   is_void: is_cv_void<const volatile T> { };
> 
> It is a bit of a compromise between the two implementations shown above.
> This has the advantage over the "alternative approach" that it reduces
> the number of templates to parse. It also eliminates the need to
> instantiate remove_cv that is used in the template metaprogramming case.

Interesting idea!

> 
> Martin, if you still have your performance numbers, could you compare
> this to the previously proposed options?

The test script I've been using most recently is here:
   http://people.apache.org/~sebor/templatetest

This script is slightly different than the test case I used to
produce the original numbers. The original test also used the
-A option with EDG eccp, while this one doesn't.

Running it like this
     $ ~/tmp/templatetest -c "eccp gcc" -i 20 -t 1000

produces these results (I modified the output to show only
user times in a M:SS format in an easy-to-read table):

     generating 20 instantiations of 1000 distinct types...
                             ECCP    GCC
                              3.9  4.3.0
     with add_cv             0:28   0:22   # using is_cv_void
     without remove_const    0:24   0:19
     with remove_const       0:49   0:18
     with inline expansion   0:39   0:18

Btw., the EDG eccp times are without the -A strict conformance
option that we use; the times with the option are usually much
longer (I'd have to modify my script to pass arguments to it
to get those results).

Martin

RE: implementation of Unary Traits

Posted by Travis Vitek <Tr...@roguewave.com>.
 

Martin Sebor wrote:
>
>Eric Lemings wrote:
>>  
>> 
>>> Martin Sebor wrote:
>>>
>>> Travis Vitek wrote:
>>>>  
>>> [...]
>>>> If you ask what I prefer, I'm going to tell you I prefer the second
>>>> option (that is essentially what I wrote originally). But, 
>>> honestly, for
>>>> me to care either way, I need to know that there actually a 
>>> noticeable
>>>> performance difference between the two techniques.
>>> FYI: I used gcc 4.3 and EDG eccp to measure the difference between
>>> the compilation times of each of the two approaches (i.e., using
>>> specialization vs using remove_cv).
>>>
>>> In a program involving 10,000 invocations of is_void on distinct
>>> types, the specialization approach was 5 and 10 times faster than
>>> the one using remove_cv when using gcc and eccp, respectively. In
>>> the same program using only 1000 types, the specialization solution
>>> compiled 2 and 3 times faster, respectively.
>>>
>>> With gcc, the compiler also required about half the amount of system
>>> memory to compile the specialization-based solution than the other
>>> one. (I didn't measure eccp memory usage).
>>>
>>> This confirms that template metaprogramming is significantly more
>>> costly in terms of system resources than alternative approaches,
>>> at least in the gcc and eccp implementations. We should re-run the
>>> same tests with other compilers to get a complete picture.
>> 
>> That's not unexpected: like everything in computing, it's a tradeoff.
>> 
>> To get a really complete picture, you'd have to compare the
>> metaprogramming approach to the run-time alternatives.
>
>There are no runtime alternatives to the Unary Traits. We are
>discussing the pros and cons of one kind of a generic program
>vs another.
>
>To illustrate on an example, I was comparing the compilation
>efficiency of this code (I called it the "alternative approach"
>in my comment):
>
>   template <class T>
>   struct is_void { typedef false_type type; };
>
>   template <>
>   struct is_void<void> { typedef true_type type; };
>
>   template <>
>   struct is_void<const void> { typedef true_type type; };
>
>   template <> struct
>   struct is_void<volatile void> { typedef true_type type; };
>
>   template <> struct
>   struct is_void<const volatile void> { typedef true_type type; };
>
>to this code (which I called "template metaprogramming"):
>
>   template <class T>
>   struct is_void_impl { typedef false_type type; };
>
>   template <> struct
>   struct is_void_impl<void> { typedef true_type type; };
>
>   template <class T>
>   is_void: is_void_impl<typename remove_cv<T>::type { };
>
>Martin
>
>

Here is one alternative that we might want to consider. Instead of
stripping cv qualifiers, we add them and then provide a specialization
for the cv-qualified type.

  template <class T>
  struct is_cv_void { typedef false_type type; };

  template <> struct
  struct is_cv_void<const volatile void> { typedef true_type type; };

  template <class T>
  is_void: is_cv_void<const volatile T> { };

It is a bit of a compromise between the two implementations shown above.
This has the advantage over the "alternative approach" that it reduces
the number of templates to parse. It also eliminates the need to
instantiate remove_cv that is used in the template metaprogramming case.

Martin, if you still have your performance numbers, could you compare
this to the previously proposed options?

Travis

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Eric Lemings wrote:
>  
> 
>> -----Original Message-----
>> From: Martin Sebor [mailto:msebor@gmail.com] On Behalf Of Martin Sebor
>> Sent: Monday, June 30, 2008 10:51 PM
>> To: dev@stdcxx.apache.org
>> Subject: Re: implementation of Unary Traits
>>
>> Travis Vitek wrote:
>>>  
>> [...]
>>> If you ask what I prefer, I'm going to tell you I prefer the second
>>> option (that is essentially what I wrote originally). But, 
>> honestly, for
>>> me to care either way, I need to know that there actually a 
>> noticeable
>>> performance difference between the two techniques.
>> FYI: I used gcc 4.3 and EDG eccp to measure the difference between
>> the compilation times of each of the two approaches (i.e., using
>> specialization vs using remove_cv).
>>
>> In a program involving 10,000 invocations of is_void on distinct
>> types, the specialization approach was 5 and 10 times faster than
>> the one using remove_cv when using gcc and eccp, respectively. In
>> the same program using only 1000 types, the specialization solution
>> compiled 2 and 3 times faster, respectively.
>>
>> With gcc, the compiler also required about half the amount of system
>> memory to compile the specialization-based solution than the other
>> one. (I didn't measure eccp memory usage).
>>
>> This confirms that template metaprogramming is significantly more
>> costly in terms of system resources than alternative approaches,
>> at least in the gcc and eccp implementations. We should re-run the
>> same tests with other compilers to get a complete picture.
> 
> That's not unexpected: like everything in computing, it's a tradeoff.
> 
> To get a really complete picture, you'd have to compare the
> metaprogramming approach to the run-time alternatives.

There are no runtime alternatives to the Unary Traits. We are
discussing the pros and cons of one kind of a generic program
vs another.

To illustrate on an example, I was comparing the compilation
efficiency of this code (I called it the "alternative approach"
in my comment):

   template <class T>
   struct is_void { typedef false_type type; };

   template <>
   struct is_void<void> { typedef true_type type; };

   template <>
   struct is_void<const void> { typedef true_type type; };

   template <> struct
   struct is_void<volatile void> { typedef true_type type; };

   template <> struct
   struct is_void<const volatile void> { typedef true_type type; };

to this code (which I called "template metaprogramming"):

   template <class T>
   struct is_void_impl { typedef false_type type; };

   template <> struct
   struct is_void_impl<void> { typedef true_type type; };

   template <class T>
   is_void: is_void_impl<typename remove_cv<T>::type { };

Martin


RE: implementation of Unary Traits

Posted by Eric Lemings <Er...@roguewave.com>.
 

> -----Original Message-----
> From: Martin Sebor [mailto:msebor@gmail.com] On Behalf Of Martin Sebor
> Sent: Monday, June 30, 2008 10:51 PM
> To: dev@stdcxx.apache.org
> Subject: Re: implementation of Unary Traits
> 
> Travis Vitek wrote:
> >  
> [...]
> > If you ask what I prefer, I'm going to tell you I prefer the second
> > option (that is essentially what I wrote originally). But, 
> honestly, for
> > me to care either way, I need to know that there actually a 
> noticeable
> > performance difference between the two techniques.
> 
> FYI: I used gcc 4.3 and EDG eccp to measure the difference between
> the compilation times of each of the two approaches (i.e., using
> specialization vs using remove_cv).
> 
> In a program involving 10,000 invocations of is_void on distinct
> types, the specialization approach was 5 and 10 times faster than
> the one using remove_cv when using gcc and eccp, respectively. In
> the same program using only 1000 types, the specialization solution
> compiled 2 and 3 times faster, respectively.
> 
> With gcc, the compiler also required about half the amount of system
> memory to compile the specialization-based solution than the other
> one. (I didn't measure eccp memory usage).
> 
> This confirms that template metaprogramming is significantly more
> costly in terms of system resources than alternative approaches,
> at least in the gcc and eccp implementations. We should re-run the
> same tests with other compilers to get a complete picture.

That's not unexpected: like everything in computing, it's a tradeoff.

To get a really complete picture, you'd have to compare the
metaprogramming approach to the run-time alternatives.  I would expect
to see a corresponding speed increase and storage savings compared to
the runtime counterparts.

Brad.

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
>  
[...]
> If you ask what I prefer, I'm going to tell you I prefer the second
> option (that is essentially what I wrote originally). But, honestly, for
> me to care either way, I need to know that there actually a noticeable
> performance difference between the two techniques.

FYI: I used gcc 4.3 and EDG eccp to measure the difference between
the compilation times of each of the two approaches (i.e., using
specialization vs using remove_cv).

In a program involving 10,000 invocations of is_void on distinct
types, the specialization approach was 5 and 10 times faster than
the one using remove_cv when using gcc and eccp, respectively. In
the same program using only 1000 types, the specialization solution
compiled 2 and 3 times faster, respectively.

With gcc, the compiler also required about half the amount of system
memory to compile the specialization-based solution than the other
one. (I didn't measure eccp memory usage).

This confirms that template metaprogramming is significantly more
costly in terms of system resources than alternative approaches,
at least in the gcc and eccp implementations. We should re-run the
same tests with other compilers to get a complete picture.

Martin


Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
>  
> 
> Martin Sebor wrote:
[...]
>> Initially, I had a slight bias for the first approach.
>> I started warming up to the second one because the code
>> reuse seems like a cleaner, better design. But now that
>> we've seen how much more costly in terms of system
>> resources during compilation the second approach is
>> I think the specialization approach might be the way to
>> go after all, not just for traits, but in general.
> 
> Yes, thanks for taking the time to evaluate this. If I had to read the
> code I'd prefer to see the first approach, but it does appear that the
> second approach (explicit specialization for each cv-qualified type) is
> going to reduce compile time costs for us and our users.
> 
>> I'd
>> still like to measure the compilation performance of the
>> first alternative to get a more complete picture.
> 
> I'm unclear what approach you are talking about here. Are you talking
> about doing more testing of the techniques using the remove_cv<> trait,
> or something else?

We've been discussing three approaches:

   1. the one that's currently implemented; let's call it the
      specialization approach

   2. one where each _RWSTD_IS_XXX() macro strips the cv-qualifiers
      from the type before passing the type to the implementation
      trait; I'll call it the macro approach

   3. one where each __rw_is_xxx trait is derived from another
      __rw_is_xxx_impl trait specialized on __rw_remove_cv<T>;
      I called it the metaprogramming approach

I have now benchmarked all three approaches using variable numbers
of types and template instantiations, and with both gcc 4.3 and EDG
eccp 3.9 on Linux. Here are the normalized results:

                           2000x10      200x100      20x1000
   APPROACH               GCC  ECCP    GCC  ECCP    GCC  ECCP
   [1] specialization     1.4   1      3.3   2.8   34.1  26.3
   [2] macro              1.8   1.2    4     3.8   48.2  49.5
   [3] metaprogramming    1.4  67.2    3.5  87.8   63.3 268.5

The column labeled 2000x10 shows normalized times for 2000
groups of implicit instantiations of is_void on 10 distinct types
with all combinations of cv-qualifications. The next column labeled
200x100 is for 200 instantiations on 100 distinct types, and the last
one, labeled 20x1000 is for 20 instantiations on 1000 distinct types.

According to the table, the specialization approach (1) yields
the best compile-time performance with both compilers. The speed
of both compilers is much more affected by the number of distinct
types (i.e., the number of generated distinct specializations of
the templates) than by the number of instantiations, suggesting
that they cache each instantiated specialization and reuse it to
satisfy the next instantiation request, although the eccp numbers
for approach (3) are a little mystifying.

Martin

RE: implementation of Unary Traits

Posted by Travis Vitek <Tr...@roguewave.com>.
 

Martin Sebor wrote:
>
>Travis Vitek wrote:
>>  
>[...]
>> Originally I wanted separate headers for each trait, but it was
>> determined that the overhead from including all of these small files
>> would be to much, so I suggested splitting traits up into 
>> groups based on what they did (<rw/_add_cv.h>, <rw/_remove_cv.h>,
>> ...) but that was vetoed also.
>
>I think there are two separate issues/questions here:
>
>   1. should the UnaryTraits make use of the remove_cv trait
>      or should they be partially specialized and remove the
>      cv qualifiers directly?
>
>   2. what is the optimal organization of individual traits
>      into headers?
>
>This thread started with question (1). While I think that
>(2) still needs to be discussed (and the decision for (1)
>has an impact on (2)), I think we might want to have the
>two discussions in separate threads. I suggest we focus
>on (1) first since I believe it's the more important of
>the two issues. I hope/expect that the right approach
>for (2) will become more clear after we've decided (1).

I think this is probably long overdue. I should have provided a set of
alternatives and prompted a vote before I committed the traits code, but
I'm sure we'll get it all ironed out.

I'll create a wiki page for this so that we can document what we've come
up with.

>> 
>[...]
>> If you ask what I prefer, I'm going to tell you I prefer the
>> second option (that is essentially what I wrote originally).
>> But, honestly, for me to care either way, I need to know that
>> there actually a noticeable performance difference between
>> the two techniques.
>
>Initially, I had a slight bias for the first approach.
>I started warming up to the second one because the code
>reuse seems like a cleaner, better design. But now that
>we've seen how much more costly in terms of system
>resources during compilation the second approach is
>I think the specialization approach might be the way to
>go after all, not just for traits, but in general.

Yes, thanks for taking the time to evaluate this. If I had to read the
code I'd prefer to see the first approach, but it does appear that the
second approach (explicit specialization for each cv-qualified type) is
going to reduce compile time costs for us and our users.

>I'd
>still like to measure the compilation performance of the
>first alternative to get a more complete picture.

I'm unclear what approach you are talking about here. Are you talking
about doing more testing of the techniques using the remove_cv<> trait,
or something else?

>
>Let me know your thoughts.
>
>Martin
>

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
>  
[...]
> Originally I wanted separate headers for each trait, but it was
> determined that the overhead from including all of these small files
> would be to much, so I suggested splitting traits up into groups based
> on what they did (<rw/_add_cv.h>, <rw/_remove_cv.h>, ...) but that was
> vetoed also.

I think there are two separate issues/questions here:

   1. should the UnaryTraits make use of the remove_cv trait
      or should they be partially specialized and remove the
      cv qualifiers directly?

   2. what is the optimal organization of individual traits
      into headers?

This thread started with question (1). While I think that
(2) still needs to be discussed (and the decision for (1)
has an impact on (2)), I think we might want to have the
two discussions in separate threads. I suggest we focus
on (1) first since I believe it's the more important of
the two issues. I hope/expect that the right approach
for (2) will become more clear after we've decided (1).

> 
[...]
> If you ask what I prefer, I'm going to tell you I prefer the second
> option (that is essentially what I wrote originally). But, honestly, for
> me to care either way, I need to know that there actually a noticeable
> performance difference between the two techniques.

Initially, I had a slight bias for the first approach.
I started warming up to the second one because the code
reuse seems like a cleaner, better design. But now that
we've seen how much more costly in terms of system
resources during compilation the second approach is
I think the specialization approach might be the way to
go after all, not just for traits, but in general. I'd
still like to measure the compilation performance of the
first alternative to get a more complete picture.

Let me know your thoughts.

Martin

RE: implementation of Unary Traits

Posted by Travis Vitek <Tr...@roguewave.com>.
 

Martin Sebor wrote:
>
>Travis Vitek wrote:
>>  
>> 
>> Martin Sebor wrote:
>>> The implementation of Unary Traits (e.g., is_void) uses explicit
>>> specialization on all four combinations of cv-qualifiers for each
>>> trait (plain, const, volatile, and const volatile). I'm wondering
>>> if the alternative approach of stripping the qualifiers before
>>> "dispatching" to just one explicit specialization has been
>>> considered. 
>> 
>> That is what I had originally done.
>
>I forgot about that.
>
>[...]
>> 
>> The only issue is that this creates a cyclic dependency 
>> between traits
>> defined in <rw/_meta_cv.h> and those in <rw/_meta_cat.h>.
>
>But only because of the __rw_add_xxx Transformation Traits. Not
>because of the __rw_is_xxx Unary Traits, correct?

Yes.

>IMO, the two
>categories belong in separate headers anyway. Not just logically
>but also (and mainly) because unlike the latter, I don't expect
>us to be needing the former in [many] other areas of the library.

I can accept that.

Originally I wanted separate headers for each trait, but it was
determined that the overhead from including all of these small files
would be to much, so I suggested splitting traits up into groups based
on what they did (<rw/_add_cv.h>, <rw/_remove_cv.h>, ...) but that was
vetoed also.

>
>> An easy way to
>> 'fix' this would be to forward declare __rw_remove_cv in
>> <rw/_meta_cat.h> and then include <rw/_meta_cv.h> at the bottom, but
>> this goes against our conventions. Another option was to put the
>> remove-cv traits into their own header, but this went against your
>> request to organize the traits in files according to some rationale.
>
>My suggested guideline is to group traits according to how likely
>they are to be used in other areas of the library.

It seems that your 'suggested guideline' is in direct conflict with your
'requirement' that the traits be arranged into files according to some
'well defined rationale'. This is the same reasoning you used to reject
my proposal of splitting related traits into separate files.

>From what I gather, you are suggesting that all traits go into
<type_traits> unless they are likely to be used in other areas of the
library. If they are 'likely' to be used in other parts of the library,
then they go into some other header(s), the names of which depend on
some yet to be determined naming scheme. So the 'well defined rationale'
does not seem to be very well defined.

>I expect the
>is_cv-kind of traits to be used pervasively (in containers and
>some algorithms). OTOH, I expect the add_cv ones to be used only
>exceedingly rarely, if ever. Traits that are not used in other
>library headers can be defined directly in <type_traits> or
>grouped in implementation-specific headers according to some
>other sensible criteria.

Okay, I've already taken two shots at this. One that I showed as an
initial implementation for review, and the other which has been
committed to subversion.

>I don't have a strong preference as
>long as they don't get pulled in to translation units
>unnecessarily.

This is in conflict with the feedback you provided on my initial traits
implementation. You didn't like that each trait was in its own header,
yet no trait was unnecessarily pulled in.

>
>> 
>>> The potential advantage of this approach is fewer
>>> declarations, smaller translation units, and thus (presumably)
>>> faster compilation.
>>>
>> 
>> There has to be a balance somewhere.
>
>Absolutely.
>
>> If all traits are in one file, we
>> have few includes, but lots of unnecessary declarations. If we spread
>> them all out, then we have few unnecessary declarations, but many
>> includes. Both can _potentially_ lead to (ever so slightly) longer
>> compile times in some cases and shorter ones in other cases.
>> 
>> I'm definitely open to suggestions and I'm willing to make 
>any necessary
>> changes. Unfortunately any suggestion has to take the 
>following criteria
>> into consideration...
>> 
>>   A. the number of trait headers should be kept to a minimum (to keep
>> compile times down)
>>   B. there should not be many unnecessary declarations in 
>any header (to
>> keep compile times down)
>>   C. traits should be put into files according to some well defined
>> rationale (to keep things organized)
>> 
>> Unfortunately, I don't really see a clear path to satisfying 
>> all of the above requirements.
>
>I can think of a couple of options that satisfy these. I'm not sure
>how palatable I find them yet, but I might get over their (initial)
>lack of appeal if the payoff is worth it. YMMV.
>
>One is to drop the cv specializations of the __rw_is_xxx traits and
>define the standard traits in terms of both remove_cv and the __rw
>traits, like so:
>
>   template <class _TypeT>
>   struct is_void:
>       integral_constant<bool,
>           _RW::__rw_is_void<typename
>               _RW::__rw_remove_cv<_TypeT>::type>::value>
>   { };
>
>or more concisely:
>
>   template <class _TypeT>
>   struct is_void:
>       integral_constant<bool, _RWSTD_IS_VOID (_TypeT)::value>
>   { };
>
>with _RWSTD_IS_VOID() being #defined like so:
>
>   #define _RWSTD_IS_VOID(T) \
>       _RW::__rw_is_void<typename _RW::__rw_remove_cv<T>::type>
>

If you do it this way, __rw_is_void<> only tells you if a type is void
or not, it doesn't tell you if the type is a possibly cv-qualified void.
I suppose that this is fine, provided you never use the __rw_is_void<>
template directly.

This also seems to require that we keep the _RWSTD_TT_* macros because
we always need to define the template types for use with the macro.
I.e., you will run into serious problems if you attempt to do something
like

  #define _RWSTD_HAS_NOTHROW_CTOR(T) \
      __has_nothrow_default_ctor(T)

  template <class _TypeT>
  struct has_nothrow_default_ctor:
      integral_constant<bool, _RWSTD_HAS_NOTHROW_CTOR (_TypeT)::value>
  { };


>All internal code would always need to use _RWSTD_IS_VOID() and
>never _RW::__rw_is_void directly.

If this is the case, then I see no motivation for
__rw_integral_constant<>.

>The other is to introduce an additional intermediate template for
>each of the __rw_is_xxx traits to remove the cv qualifiers:
>
>     template <class _TypeT>
>     struct __rw_is_void_impl: __rw_false_type { };
>
>     template <>
>     struct __rw_is_void_impl<void>: __rw_true_type { };
>
>     template <class _TypeT>
>     struct __rw_is_void:
>         __rw_is_void_impl<typename __rw_remove_cv<_TypeT>::type>
>     { };
>
>The first alternative is definitely the cheapest in terms of lines
>of new code required to implement it, but I know that not everyone
>is a fan of macros. The redeeming fact is that IIRC we were planning
>to use the _RWSTD_IS_XXX() macros anyway, so this change shouldn't
>be a problem.
>
>The second one seems somewhat "cleaner" but I suspect adding the
>additional template might cancel out the savings gained by removing
>the explicit specialization.
>
>Let me know what you think.

If you ask what I prefer, I'm going to tell you I prefer the second
option (that is essentially what I wrote originally). But, honestly, for
me to care either way, I need to know that there actually a noticeable
performance difference between the two techniques.

>
>Martin
>

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
>  
> 
> Martin Sebor wrote:
>> The implementation of Unary Traits (e.g., is_void) uses explicit
>> specialization on all four combinations of cv-qualifiers for each
>> trait (plain, const, volatile, and const volatile). I'm wondering
>> if the alternative approach of stripping the qualifiers before
>> "dispatching" to just one explicit specialization has been
>> considered. 
> 
> That is what I had originally done.

I forgot about that.

[...]
> 
> The only issue is that this creates a cyclic dependency between traits
> defined in <rw/_meta_cv.h> and those in <rw/_meta_cat.h>.

But only because of the __rw_add_xxx Transformation Traits. Not
because of the __rw_is_xxx Unary Traits, correct? IMO, the two
categories belong in separate headers anyway. Not just logically
but also (and mainly) because unlike the latter, I don't expect
us to be needing the former in [many] other areas of the library.

> An easy way to
> 'fix' this would be to forward declare __rw_remove_cv in
> <rw/_meta_cat.h> and then include <rw/_meta_cv.h> at the bottom, but
> this goes against our conventions. Another option was to put the
> remove-cv traits into their own header, but this went against your
> request to organize the traits in files according to some rationale.

My suggested guideline is to group traits according to how likely
they are to be used in other areas of the library. I expect the
is_cv-kind of traits to be used pervasively (in containers and
some algorithms). OTOH, I expect the add_cv ones to be used only
exceedingly rarely, if ever. Traits that are not used in other
library headers can be defined directly in <type_traits> or
grouped in implementation-specific headers according to some
other sensible criteria. I don't have a strong preference as
long as they don't get pulled in to translation units
unnecessarily.

> 
>> The potential advantage of this approach is fewer
>> declarations, smaller translation units, and thus (presumably)
>> faster compilation.
>>
> 
> There has to be a balance somewhere.

Absolutely.

> If all traits are in one file, we
> have few includes, but lots of unnecessary declarations. If we spread
> them all out, then we have few unnecessary declarations, but many
> includes. Both can _potentially_ lead to (ever so slightly) longer
> compile times in some cases and shorter ones in other cases.
> 
> I'm definitely open to suggestions and I'm willing to make any necessary
> changes. Unfortunately any suggestion has to take the following criteria
> into consideration...
> 
>   A. the number of trait headers should be kept to a minimum (to keep
> compile times down)
>   B. there should not be many unnecessary declarations in any header (to
> keep compile times down)
>   C. traits should be put into files according to some well defined
> rationale (to keep things organized)
> 
> Unfortunately, I don't really see a clear path to satisfying all of the
> above requirements.

I can think of a couple of options that satisfy these. I'm not sure
how palatable I find them yet, but I might get over their (initial)
lack of appeal if the payoff is worth it. YMMV.

One is to drop the cv specializations of the __rw_is_xxx traits and
define the standard traits in terms of both remove_cv and the __rw
traits, like so:

   template <class _TypeT>
   struct is_void:
       integral_constant<bool,
           _RW::__rw_is_void<typename
               _RW::__rw_remove_cv<_TypeT>::type>::value>
   { };

or more concisely:

   template <class _TypeT>
   struct is_void:
       integral_constant<bool, _RWSTD_IS_VOID (_TypeT)::value>
   { };

with _RWSTD_IS_VOID() being #defined like so:

   #define _RWSTD_IS_VOID(T) \
       _RW::__rw_is_void<typename _RW::__rw_remove_cv<T>::type>

All internal code would always need to use _RWSTD_IS_VOID() and
never _RW::__rw_is_void directly.

The other is to introduce an additional intermediate template for
each of the __rw_is_xxx traits to remove the cv qualifiers:

     template <class _TypeT>
     struct __rw_is_void_impl: __rw_false_type { };

     template <>
     struct __rw_is_void_impl<void>: __rw_true_type { };

     template <class _TypeT>
     struct __rw_is_void:
         __rw_is_void_impl<typename __rw_remove_cv<_TypeT>::type>
     { };

The first alternative is definitely the cheapest in terms of lines
of new code required to implement it, but I know that not everyone
is a fan of macros. The redeeming fact is that IIRC we were planning
to use the _RWSTD_IS_XXX() macros anyway, so this change shouldn't
be a problem.

The second one seems somewhat "cleaner" but I suspect adding the
additional template might cancel out the savings gained by removing
the explicit specialization.

Let me know what you think.

Martin

RE: implementation of Unary Traits

Posted by Travis Vitek <Tr...@roguewave.com>.
 

Martin Sebor wrote:
>
>The implementation of Unary Traits (e.g., is_void) uses explicit
>specialization on all four combinations of cv-qualifiers for each
>trait (plain, const, volatile, and const volatile). I'm wondering
>if the alternative approach of stripping the qualifiers before
>"dispatching" to just one explicit specialization has been
>considered. 

That is what I had originally done. The following code may be eerily
familiar to you.

  #include <tr1/_remove_cv.h>
  #include <rw/_defs.h>

  _RWSTD_NAMESPACE (__rw) {

  template <class _TypeT> struct __rw_is_void_impl
  {
      enum { _C_value = 0 };
  };

  _RWSTD_SPECIALIZED_CLASS struct __rw_is_void_impl<void>
  {
      enum { _C_value = 1 };
  };
    
  template <class _TypeT> struct __rw_is_void
  {
      enum { _C_value = 
          __rw_is_void_impl<
              _TYPENAME __rw_remove_cv<_TypeT>::_C_type
          >::_C_value
      };
  };

  } // namespace __rw

The only issue is that this creates a cyclic dependency between traits
defined in <rw/_meta_cv.h> and those in <rw/_meta_cat.h>. An easy way to
'fix' this would be to forward declare __rw_remove_cv in
<rw/_meta_cat.h> and then include <rw/_meta_cv.h> at the bottom, but
this goes against our conventions. Another option was to put the
remove-cv traits into their own header, but this went against your
request to organize the traits in files according to some rationale.

>The potential advantage of this approach is fewer
>declarations, smaller translation units, and thus (presumably)
>faster compilation.
>

There has to be a balance somewhere. If all traits are in one file, we
have few includes, but lots of unnecessary declarations. If we spread
them all out, then we have few unnecessary declarations, but many
includes. Both can _potentially_ lead to (ever so slightly) longer
compile times in some cases and shorter ones in other cases.

I'm definitely open to suggestions and I'm willing to make any necessary
changes. Unfortunately any suggestion has to take the following criteria
into consideration...

  A. the number of trait headers should be kept to a minimum (to keep
compile times down)
  B. there should not be many unnecessary declarations in any header (to
keep compile times down)
  C. traits should be put into files according to some well defined
rationale (to keep things organized)

Unfortunately, I don't really see a clear path to satisfying all of the
above requirements.

>Martin
>

Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Travis Vitek wrote:
> Sorry for top posting...
> 
> Yes, the __rw_is_convertible_impl<T,U>::_C_make should probably be changed to return __rw_remove_cv<_TypeT>::type (probably with a typedef). We should probably add a case for this in the 20.meta.rel.cpp test also.
> 
> I'm on vacation today otherwise I'd make the necessary changes myself.

Note: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36656

> 
> Travis
> 
> 
> -----Original Message-----
> From: Martin Sebor on behalf of Martin Sebor
> Sent: Thu 6/26/2008 10:12 PM
> To: dev@stdcxx.apache.org
> Subject: Re: implementation of Unary Traits
>  
> Eric Lemings wrote:
>>  
>>
>>> -----Original Message-----
>>> From: Martin Sebor [mailto:msebor@gmail.com] On Behalf Of Martin Sebor
>>> Sent: Thursday, June 26, 2008 5:45 PM
>>> To: dev@stdcxx.apache.org
>>> Subject: implementation of Unary Traits
>>>
>>> The implementation of Unary Traits (e.g., is_void) uses explicit
>>> specialization on all four combinations of cv-qualifiers for each
>>> trait (plain, const, volatile, and const volatile). I'm wondering
>>> if the alternative approach of stripping the qualifiers before
>>> "dispatching" to just one explicit specialization has been
>>> considered. The potential advantage of this approach is fewer
>>> declarations, smaller translation units, and thus (presumably)
>>> faster compilation.
>> Though I'm using a relational (binary) type trait, I'm getting warnings
>> because the cv-qualifiers are not being stripped.
> 
> A test case is always helpful:
> 
> #include <type_traits>
> 
> int main ()
> {
>      return !std::is_convertible<const int, int>::value;
> }
> 
> Seems like is_convertible might need to strip top-level cv qualifiers
> from the types. What do you think, Travis?
> 
> Martin
> 
>> 	gcc -c -I/work/stdcxx/branches/4.3.x/include/ansi -D_RWSTDDEBUG
>> -pthread -I/work/stdcxx/branches/4.3.x/include
>> -I/build/stdcxx-4.3.x-15D/include
>> -I/work/stdcxx/branches/4.3.x/tests/include  -std=gnu++0x
>> -D_RWSTD_EXT_CXX_0X -W -Wall -Wcast-qual -Winline -Wshadow
>> -Wwrite-strings -Wno-long-long -Wcast-align
>> /work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp
>> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h: In
>> instantiation of '__rw::__rw_is_convertible_impl<const int, int>':
>> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:93:
>> instantiated from '__rw::__rw_is_convertible_3<const int, int, false,
>> false>'
>> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:122:
>> instantiated from '__rw::__rw_is_convertible_2<const int, int, false>'
>> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:145:
>> instantiated from '__rw::__rw_is_convertible_1<const int, int, false,
>> false>'
>> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:163:
>> instantiated from '__rw::__rw_is_convertible<const int, int>'
>> 	/work/stdcxx/branches/4.3.x/include/tuple:68:   instantiated
>> from 'std::tuple<const int>::_C_is_compatible<int>'
>> 	/work/stdcxx/branches/4.3.x/include/tuple:123:   instantiated
>> from 'std::tuple<_Types>::tuple(_TypesU&& ...) [with _TypesU = int,
>> _TypesT = const int]'
>> 	
>> /work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp:103:
>> instantiated from here
>> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:77: warning:
>> type qualifiers ignored on function return type
>>
>> Brad.
> 
> 


RE: implementation of Unary Traits

Posted by Travis Vitek <Tr...@roguewave.com>.
Sorry for top posting...

Yes, the __rw_is_convertible_impl<T,U>::_C_make should probably be changed to return __rw_remove_cv<_TypeT>::type (probably with a typedef). We should probably add a case for this in the 20.meta.rel.cpp test also.

I'm on vacation today otherwise I'd make the necessary changes myself.

Travis


-----Original Message-----
From: Martin Sebor on behalf of Martin Sebor
Sent: Thu 6/26/2008 10:12 PM
To: dev@stdcxx.apache.org
Subject: Re: implementation of Unary Traits
 
Eric Lemings wrote:
>  
> 
>> -----Original Message-----
>> From: Martin Sebor [mailto:msebor@gmail.com] On Behalf Of Martin Sebor
>> Sent: Thursday, June 26, 2008 5:45 PM
>> To: dev@stdcxx.apache.org
>> Subject: implementation of Unary Traits
>>
>> The implementation of Unary Traits (e.g., is_void) uses explicit
>> specialization on all four combinations of cv-qualifiers for each
>> trait (plain, const, volatile, and const volatile). I'm wondering
>> if the alternative approach of stripping the qualifiers before
>> "dispatching" to just one explicit specialization has been
>> considered. The potential advantage of this approach is fewer
>> declarations, smaller translation units, and thus (presumably)
>> faster compilation.
> 
> Though I'm using a relational (binary) type trait, I'm getting warnings
> because the cv-qualifiers are not being stripped.

A test case is always helpful:

#include <type_traits>

int main ()
{
     return !std::is_convertible<const int, int>::value;
}

Seems like is_convertible might need to strip top-level cv qualifiers
from the types. What do you think, Travis?

Martin

> 
> 	gcc -c -I/work/stdcxx/branches/4.3.x/include/ansi -D_RWSTDDEBUG
> -pthread -I/work/stdcxx/branches/4.3.x/include
> -I/build/stdcxx-4.3.x-15D/include
> -I/work/stdcxx/branches/4.3.x/tests/include  -std=gnu++0x
> -D_RWSTD_EXT_CXX_0X -W -Wall -Wcast-qual -Winline -Wshadow
> -Wwrite-strings -Wno-long-long -Wcast-align
> /work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h: In
> instantiation of '__rw::__rw_is_convertible_impl<const int, int>':
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:93:
> instantiated from '__rw::__rw_is_convertible_3<const int, int, false,
> false>'
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:122:
> instantiated from '__rw::__rw_is_convertible_2<const int, int, false>'
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:145:
> instantiated from '__rw::__rw_is_convertible_1<const int, int, false,
> false>'
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:163:
> instantiated from '__rw::__rw_is_convertible<const int, int>'
> 	/work/stdcxx/branches/4.3.x/include/tuple:68:   instantiated
> from 'std::tuple<const int>::_C_is_compatible<int>'
> 	/work/stdcxx/branches/4.3.x/include/tuple:123:   instantiated
> from 'std::tuple<_Types>::tuple(_TypesU&& ...) [with _TypesU = int,
> _TypesT = const int]'
> 	
> /work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp:103:
> instantiated from here
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:77: warning:
> type qualifiers ignored on function return type
> 
> Brad.



Re: implementation of Unary Traits

Posted by Martin Sebor <se...@roguewave.com>.
Eric Lemings wrote:
>  
> 
>> -----Original Message-----
>> From: Martin Sebor [mailto:msebor@gmail.com] On Behalf Of Martin Sebor
>> Sent: Thursday, June 26, 2008 5:45 PM
>> To: dev@stdcxx.apache.org
>> Subject: implementation of Unary Traits
>>
>> The implementation of Unary Traits (e.g., is_void) uses explicit
>> specialization on all four combinations of cv-qualifiers for each
>> trait (plain, const, volatile, and const volatile). I'm wondering
>> if the alternative approach of stripping the qualifiers before
>> "dispatching" to just one explicit specialization has been
>> considered. The potential advantage of this approach is fewer
>> declarations, smaller translation units, and thus (presumably)
>> faster compilation.
> 
> Though I'm using a relational (binary) type trait, I'm getting warnings
> because the cv-qualifiers are not being stripped.

A test case is always helpful:

#include <type_traits>

int main ()
{
     return !std::is_convertible<const int, int>::value;
}

Seems like is_convertible might need to strip top-level cv qualifiers
from the types. What do you think, Travis?

Martin

> 
> 	gcc -c -I/work/stdcxx/branches/4.3.x/include/ansi -D_RWSTDDEBUG
> -pthread -I/work/stdcxx/branches/4.3.x/include
> -I/build/stdcxx-4.3.x-15D/include
> -I/work/stdcxx/branches/4.3.x/tests/include  -std=gnu++0x
> -D_RWSTD_EXT_CXX_0X -W -Wall -Wcast-qual -Winline -Wshadow
> -Wwrite-strings -Wno-long-long -Wcast-align
> /work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h: In
> instantiation of '__rw::__rw_is_convertible_impl<const int, int>':
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:93:
> instantiated from '__rw::__rw_is_convertible_3<const int, int, false,
> false>'
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:122:
> instantiated from '__rw::__rw_is_convertible_2<const int, int, false>'
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:145:
> instantiated from '__rw::__rw_is_convertible_1<const int, int, false,
> false>'
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:163:
> instantiated from '__rw::__rw_is_convertible<const int, int>'
> 	/work/stdcxx/branches/4.3.x/include/tuple:68:   instantiated
> from 'std::tuple<const int>::_C_is_compatible<int>'
> 	/work/stdcxx/branches/4.3.x/include/tuple:123:   instantiated
> from 'std::tuple<_Types>::tuple(_TypesU&& ...) [with _TypesU = int,
> _TypesT = const int]'
> 	
> /work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp:103:
> instantiated from here
> 	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:77: warning:
> type qualifiers ignored on function return type
> 
> Brad.


RE: implementation of Unary Traits

Posted by Eric Lemings <Er...@roguewave.com>.
 

> -----Original Message-----
> From: Martin Sebor [mailto:msebor@gmail.com] On Behalf Of Martin Sebor
> Sent: Thursday, June 26, 2008 5:45 PM
> To: dev@stdcxx.apache.org
> Subject: implementation of Unary Traits
> 
> The implementation of Unary Traits (e.g., is_void) uses explicit
> specialization on all four combinations of cv-qualifiers for each
> trait (plain, const, volatile, and const volatile). I'm wondering
> if the alternative approach of stripping the qualifiers before
> "dispatching" to just one explicit specialization has been
> considered. The potential advantage of this approach is fewer
> declarations, smaller translation units, and thus (presumably)
> faster compilation.

Though I'm using a relational (binary) type trait, I'm getting warnings
because the cv-qualifiers are not being stripped.

	gcc -c -I/work/stdcxx/branches/4.3.x/include/ansi -D_RWSTDDEBUG
-pthread -I/work/stdcxx/branches/4.3.x/include
-I/build/stdcxx-4.3.x-15D/include
-I/work/stdcxx/branches/4.3.x/tests/include  -std=gnu++0x
-D_RWSTD_EXT_CXX_0X -W -Wall -Wcast-qual -Winline -Wshadow
-Wwrite-strings -Wno-long-long -Wcast-align
/work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp
	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h: In
instantiation of '__rw::__rw_is_convertible_impl<const int, int>':
	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:93:
instantiated from '__rw::__rw_is_convertible_3<const int, int, false,
false>'
	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:122:
instantiated from '__rw::__rw_is_convertible_2<const int, int, false>'
	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:145:
instantiated from '__rw::__rw_is_convertible_1<const int, int, false,
false>'
	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:163:
instantiated from '__rw::__rw_is_convertible<const int, int>'
	/work/stdcxx/branches/4.3.x/include/tuple:68:   instantiated
from 'std::tuple<const int>::_C_is_compatible<int>'
	/work/stdcxx/branches/4.3.x/include/tuple:123:   instantiated
from 'std::tuple<_Types>::tuple(_TypesU&& ...) [with _TypesU = int,
_TypesT = const int]'
	
/work/stdcxx/branches/4.3.x/tests/utilities/20.tuple.cnstr.cpp:103:
instantiated from here
	/work/stdcxx/branches/4.3.x/include/rw/_meta_rel.h:77: warning:
type qualifiers ignored on function return type

Brad.