Portable list of unsigned integer types

F

Fred Zwarts

I have created a template function for a generic algorithm.
For unsigned integer types, a small modification in the algorithm is needed,
because I can't use negative values there.
So, I want to make specializations for all unsigned integer types.
I assume that it is not possible in C++ to write one specialization for all
unsigned integer type, but that a specialization for each unsigned integer type
must be created.
Since this software runs on different platforms, I wonder whether it is
possible to write a complete set of specializations in a platform independent way.

I first tried to write specializations for uint8_t, uint16_t, uint32_t and uint64_t
as defined in inttypes.h, but it turns out that on some platforms some
unsigned integer types are still missing. (Under Windows e.g.,
there is more than one 32-bit unsigned integer type.)
Types like size_t and clock_t map to different types on different platforms.
I wonder whether it is possible to create a complete list,
without using conditional code selection in the preprocessor.
Is the list "unsigned char, unsigned short, unsigned int, unsigned long and unsigned long long"
complete and without overlap (as far as specializations concern) for all platforms?
Should I add unsigned wchar_t, or is it covered already?

(I know that char needs special attention. I know how to handle that case.)
 
F

Francesco S. Carta

Fred Zwarts said:
I have created a template function for a generic algorithm.
For unsigned integer types, a small modification in the algorithm is needed,
because I can't use negative values there.
So, I want to make specializations for all unsigned integer types.
I assume that it is not possible in C++ to write one specialization for all
unsigned integer type, but that a specialization for each unsigned integer type
must be created.
Since this software runs on different platforms, I wonder whether it is
possible to write a complete set of specializations in a platform independent way.

I first tried to write specializations for uint8_t, uint16_t, uint32_t and uint64_t
as defined in inttypes.h, but it turns out that on some platforms some
unsigned integer types are still missing. (Under Windows e.g.,
there is more than one 32-bit unsigned integer type.)
Types like size_t and clock_t map to different types on different platforms.
I wonder whether it is possible to create a complete list,
without using conditional code selection in the preprocessor.
Is the list "unsigned char, unsigned short, unsigned int, unsigned long and unsigned long long"
complete and without overlap (as far as specializations concern) for all platforms?
Should I add unsigned wchar_t, or is it covered already?

(I know that char needs special attention. I know how to handle that case.)

Your list seems exhaustive, and "unsigned wchar_t" should be already
covered by your list (after all, it should be just an unsigned integer
somewhat bigger than char). But you'll eventually get more certain
answers about this.

About avoiding to create all those specializations, can you get along
with duplicating the body of your template to include both algorithms,
checking then for unsigned-ness at runtime via numeric_limits?

The compiler could then optimize away the algorithm version that
doesn't happen to be appropriate for each type - at least, I suppose
so, I'd have to check if it works but I'm not so sure I will able to.

Just an idea off the top of my head.
 
F

Francesco S. Carta

Your list seems exhaustive, and "unsigned wchar_t" should be already
covered by your list (after all, it should be just an unsigned integer
somewhat bigger than char). But you'll eventually get more certain
answers about this.

About avoiding to create all those specializations, can you get along
with duplicating the body of your template to include both algorithms,
checking then for unsigned-ness at runtime via numeric_limits?

The compiler could then optimize away the algorithm version that
doesn't happen to be appropriate for each type - at least, I suppose
so, I'd have to check if it works but I'm not so sure I will able to.

Sorry, I badly worded it because it's not that clear to me. Maybe that
check will be performed at compile-time (otherwise, the compiles
wouldn't be able to optimize away those parts).

Something new to experiment ;-)
 
K

Kai-Uwe Bux

Fred said:
I have created a template function for a generic algorithm.
For unsigned integer types, a small modification in the algorithm is
needed, because I can't use negative values there.
So, I want to make specializations for all unsigned integer types.

Maybe the following helps:

template < typename T >
struct is_signed {
static bool const value = 0 > T(-1);
};
I assume that it is not possible in C++ to write one specialization for
all unsigned integer type, but that a specialization for each unsigned
integer type must be created.

No, but you could do:

template < typename T, bool IsSigned = is_signed<T>::value >
struct ...

and partially specialize that for <T,true> and <T,false>.

[...]


Best

Kai-Uwe Bux
 
F

Fred Zwarts

Kai-Uwe Bux said:
Fred said:
I have created a template function for a generic algorithm.
For unsigned integer types, a small modification in the algorithm is
needed, because I can't use negative values there.
So, I want to make specializations for all unsigned integer types.

Maybe the following helps:

template < typename T >
struct is_signed {
static bool const value = 0 > T(-1);
};
I assume that it is not possible in C++ to write one specialization
for all unsigned integer type, but that a specialization for each
unsigned integer type must be created.

No, but you could do:

template < typename T, bool IsSigned = is_signed<T>::value >
struct ...

and partially specialize that for <T,true> and <T,false>.

[...]

I tried something along these lines, but it turned out to my surprise,
that partial specializations are not allowed for template functions,
only for template classes/structs. Of course I could encapsulate the
function in a class and add a wrapper function...
 
F

Fred Zwarts

Francesco said:
Your list seems exhaustive, and "unsigned wchar_t" should be already
covered by your list (after all, it should be just an unsigned integer
somewhat bigger than char). But you'll eventually get more certain
answers about this.

About avoiding to create all those specializations, can you get along
with duplicating the body of your template to include both algorithms,
checking then for unsigned-ness at runtime via numeric_limits?

The compiler could then optimize away the algorithm version that
doesn't happen to be appropriate for each type - at least, I suppose
so, I'd have to check if it works but I'm not so sure I will able to.

Just an idea off the top of my head.

Thanks for the suggestion. I tried something along these lines.
It compiles and works as expected, but some compilers generate a lot
of warnings for those lines where tests are made for the sign of the
values during instantiations of unsigned types. Since this header is
included in may modules, I get many warnings which I cannot suppress
and which makes it difficult to find the more relevant warnings.
 
K

Kai-Uwe Bux

Fred said:
Kai-Uwe Bux wrote: [...]
No, but you could do:

template < typename T, bool IsSigned = is_signed<T>::value >
struct ...

and partially specialize that for <T,true> and <T,false>.

[...]

I tried something along these lines, but it turned out to my surprise,
that partial specializations are not allowed for template functions,
only for template classes/structs. Of course I could encapsulate the
function in a class and add a wrapper function...

Yup. That is annoying. Maybe the most prominent case is specializing a
generic algorithm to take advantage of random access iterators. You have to
wrap the algorithm as a static member function of a class template, which
you can specialize in turn. I don't know why one has to go through the
extra complexity. (Come to think of it, I don't know whether this is slated
to change in C++0X.)


Best

Kai-Uwe Bux
 
F

Francesco S. Carta

Fred Zwarts said:
Thanks for the suggestion. I tried something along these lines.
It compiles and works as expected, but some compilers generate a lot
of warnings for those lines where tests are made for the sign of the
values during instantiations of unsigned types. Since this header is
included in may modules, I get many warnings which I cannot suppress
and which makes it difficult to find the more relevant warnings.

I didn't think about those warnings.

Just in case: once you're assured that unsigned types will never make
it in your signed algo, couldn't you throw away those tests for
negativeness in the signed algo? Or perhaps change them so that they
don't raise warnings, if they're really needed.

For example, a check for some value being not negative can be
expressed as:
-------
if(val > -1) { /*...*/ };
-------

But also as either:
-------
if(val >= 0) { /* ... */ };
if(!(val < 0)) { /* ... */ };
-------

The latter two shouldn't raise any warning even if applied to unsigned
types, mmm, no, the compiler will eventually warn that those tests
always evaluate "true"...

Maybe the only way to avoid those warnings and taking advantage of the
templates is wrapping those algos within template classes, but as you
have already noted with Kai-Uwe, this is going to increase complexity
- and notation, too, I suspect.

Wait: what about this:
-------
if(val == 0 || val > 0) { /* ... */ };
-------

The above should issue no warning at all! Am I correct?
Compilers cannot be _that_ clever, can they?

Ahh. If you happen to find a compiler that warns on the above, try
this:
-------
T zero = 0; /* note: non-const... could a "volatile" help too, here?
*/
if(val >= zero) { /* ... */ };
-------

Anyway, gcc 3.4.5 never warned me for "always true/false" conditionals
- with "-Wall -pedantic" would them have been needed.

To avoid the weird part of my suggestion (duplicating the code in the
template body) one could make two different templates and wrap the
decision in a third one:
-------
#include <limits>

template<class T> void signed_algo(const T& one, const T& two) {
// ...
}

template<class T> void unsigned_algo(const T& one, const T& two) {
// ...
}

template<class T> void algo(const T& one, const T& two) {
if(std::numeric_limits<T>::is_signed) {
signed_algo(one, two);
} else {
unsigned_algo(one, two);
}
}
 
J

James Kanze

Kai-Uwe Bux said:
Fred Zwarts wrote:
I have created a template function for a generic algorithm. For
unsigned integer types, a small modification in the algorithm is
needed, because I can't use negative values there. So, I want to
make specializations for all unsigned integer types.
Maybe the following helps:
template < typename T >
struct is_signed {
static bool const value = 0 > T(-1);
};
I assume that it is not possible in C++ to write one specialization
for all unsigned integer type, but that a specialization for each
unsigned integer type must be created.
No, but you could do:
template < typename T, bool IsSigned = is_signed<T>::value >
struct ...
and partially specialize that for <T,true> and <T,false>.
[...]
I tried something along these lines, but it turned out to my surprise,
that partial specializations are not allowed for template functions,
only for template classes/structs. Of course I could encapsulate the
function in a class and add a wrapper function...

Or you could forward to an overloaded function, something like:

template< bool > class Discrim {};

template< typename T >
void helper( T value, Discrim< false > )
{
// signed...
}

template< typename T >
void helper( T value, Discrim< true > )
{
// unsigned...
}

template< typename T >
void function( T value )
{
helper( value, Discrim< is_signed< T >::value >() ) ;
}

If you're doing a lot of this (or even more than once), you'll have
Discrim already written, and maybe even is_signed, so all that's left
is
the two overloads. (I don't claim that this is a better or a worse
solution than the partial specializations suggested by Kai-Uwe---I
generally use his method myself. But it's an alternative you might
consider.)
 
J

James Kanze

Fred said:
Kai-Uwe Bux wrote: [...]
No, but you could do:
template < typename T, bool IsSigned = is_signed<T>::value >
struct ...
and partially specialize that for <T,true> and <T,false>.
[...]
I tried something along these lines, but it turned out to my
surprise, that partial specializations are not allowed for template
functions, only for template classes/structs. Of course I could
encapsulate the function in a class and add a wrapper function...
Yup. That is annoying. Maybe the most prominent case is specializing a
generic algorithm to take advantage of random access iterators. You
have to wrap the algorithm as a static member function of a class
template, which you can specialize in turn.

Why don't you do like is done in most of the standard libraries:
overload the function with an additional argument:

template< typename RandomIterator >
void f( RandomIterator begin,
RandomIterator end,
std::random_access_iterator_tag )
{
}

template< typename OtherIterator >
void f( OtherIterator begin,
OtherIterator end,
std::input_iterator_tag )
{
}

template< typename Iterator >
void f( Iterator begin, Iterator end )
{
f( begin, end,
typename std::iterator_traits< Iterator
::iterator_category() );
}

(I'm not sure that it's really that much simpler, but it is what I've
seen in the standard library. Note too that it does take advantage of
the inheritance in the iterator tags, so that a forward iterator will
automatically end up in the second overload, without having to
explicitly provide for it.)
 
J

James Kanze

Are there any particular advantages to this reinvention of the
std::numeric_limits<ClassT>::is_signed wheel?

Technically, the only instantiations of numeric_limits that are
required are those for the standard numeric types; there's no
guarantee that e.g. numeric_limits< uptrint_t > or
numeric_limits< UserDefinedInt > exist. From a QoI point of
view, I would certainly expect the first; as to the second, a
well written library will provide it, but it's the sort of thing
I'd be hesitant about counting on.
 
K

Kai-Uwe Bux

James said:
Fred said:
Kai-Uwe Bux wrote: [...]
No, but you could do:
template < typename T, bool IsSigned = is_signed<T>::value >
struct ...
and partially specialize that for <T,true> and <T,false>.
[...]
I tried something along these lines, but it turned out to my
surprise, that partial specializations are not allowed for template
functions, only for template classes/structs. Of course I could
encapsulate the function in a class and add a wrapper function...
Yup. That is annoying. Maybe the most prominent case is specializing a
generic algorithm to take advantage of random access iterators. You
have to wrap the algorithm as a static member function of a class
template, which you can specialize in turn.

Why don't you do like is done in most of the standard libraries:
overload the function with an additional argument:

template< typename RandomIterator >
void f( RandomIterator begin,
RandomIterator end,
std::random_access_iterator_tag )
{
}

template< typename OtherIterator >
void f( OtherIterator begin,
OtherIterator end,
std::input_iterator_tag )
{
}

template< typename Iterator >
void f( Iterator begin, Iterator end )
{
f( begin, end,
typename std::iterator_traits< Iterator
::iterator_category() );
}

(I'm not sure that it's really that much simpler, but it is what I've
seen in the standard library. Note too that it does take advantage of
the inheritance in the iterator tags, so that a forward iterator will
automatically end up in the second overload, without having to
explicitly provide for it.)

That's cute. I had not thaught of that.


Thanks

Kai-Uwe Bux
 
J

James Kanze

I would expect that any typedefs will get bound to the
underlying types:

They will, but the underlying type may be an extended integral
type, e.g.:
typedef __uint64 uprtint_t;
or something similar. As I said, in such cases, I would expect
(from a QoI point of view) that the implementation also provide
a specialization for the underlying type; I think the next
version of the standard (which officializes such extended
integral types) will require it. For the moment, however, we're
in a sort of a transitional period, where it sometimes occurs
that types are only partially supported---I've definitely used
compilers where the compiler itself knew about long long, but it
didn't work with anything in the library (including
numeric_limits). It's an awkward situation, but it's one we
have to live with at present.
 
F

Francesco S. Carta

James Kanze said:
Kai-Uwe Bux said:
Fred Zwarts wrote:
I have created a template function for a generic algorithm.  For
unsigned integer types, a small modification in the algorithm is
needed, because I can't use negative values there.  So, I want to
make specializations for all unsigned integer types.
Maybe the following helps:
 template < typename T >
 struct is_signed {
   static bool const value = 0 > T(-1);
 };
I assume that it is not possible in C++ to write one specialization
for all unsigned integer type, but that a specialization for each
unsigned integer type must be created.
No, but you could do:
 template < typename T, bool IsSigned = is_signed<T>::value >
 struct ...
and partially specialize that for <T,true> and <T,false>.
[...]
I tried something along these lines, but it turned out to my surprise,
that partial specializations are not allowed for template functions,
only for template classes/structs. Of course I could encapsulate the
function in a class and add a wrapper function...

Or you could forward to an overloaded function, something like:

    template< bool > class Discrim {};

    template< typename T >
    void helper( T value, Discrim< false > )
    {
        //  signed...
    }

    template< typename T >
    void helper( T value, Discrim< true > )
    {
        //  unsigned...
    }

    template< typename T >
    void function( T value )
    {
        helper( value, Discrim< is_signed< T >::value >() ) ;
    }

If you're doing a lot of this (or even more than once), you'll have
Discrim already written, and maybe even is_signed, so all that's left
is
the two overloads.  (I don't claim that this is a better or a worse
solution than the partial specializations suggested by Kai-Uwe---I
generally use his method myself.  But it's an alternative you might
consider.)

Really good to learn about these "tricks" - let's call them so - to
overcome the lack of partial specialization for functions.

Thanks to both James and Kai-Uwe.

It's always instructive to read this NG.
 
M

Maxim Yegorushkin

On 13/10/09 14:17, Francesco S. Carta wrote:

[]
To avoid the weird part of my suggestion (duplicating the code in the
template body) one could make two different templates and wrap the
decision in a third one:
-------
#include<limits>

template<class T> void signed_algo(const T& one, const T& two) {
// ...
}

template<class T> void unsigned_algo(const T& one, const T& two) {
// ...
}

template<class T> void algo(const T& one, const T& two) {
if(std::numeric_limits<T>::is_signed) {
signed_algo(one, two);
} else {
unsigned_algo(one, two);
}
}

An occasional reader is here ;)

Noticed that algo() uses a runtime if() statement which causes both
signed_algo and unsigned_algo to be instantiated for every T. The
optimizer will determine that std::numeric_limits<T>::is_signed is a
compile-time constant and throw away the dead branch, but still both
templates are instantiated.

It is easy to eliminate the unnecessary instantiation and make the
compiler instantiate only the template function which is actually called:

#include <limits>

template<bool> struct Bool {};
typedef Bool<true> True;
typedef Bool<false> False;

template<class T>
void algo(const T& one, const T& two, /*signed*/ True) {
// ...
}

template<class T>
void algo(const T& one, const T& two, /*signed*/ False) {
// ...
}

template<class T> void algo(const T& one, const T& two) {
algo(one, two, Bool<std::numeric_limits<T>::is_signed>());
}

int main()
{
algo(1, 1);
algo(1u, 1u);
}
 
F

Francesco S. Carta

Maxim Yegorushkin said:
On 13/10/09 14:17, Francesco S. Carta wrote:

[]


To avoid the weird part of my suggestion (duplicating the code in the
template body) one could make two different templates and wrap the
decision in a third one:
template<class T>  void signed_algo(const T&  one, const T&  two) {
     // ...
}
template<class T>  void unsigned_algo(const T&  one, const T&  two) {
     // ...
}
template<class T>  void algo(const T&  one, const T&  two) {
     if(std::numeric_limits<T>::is_signed) {
         signed_algo(one, two);
     } else {
         unsigned_algo(one, two);
     }
}
-------
Well, you surely have considered all of this already, I'm posting it
just for the occasional reader.

An occasional reader is here ;)

Noticed that algo() uses a runtime if() statement which causes both
signed_algo and unsigned_algo to be instantiated for every T. The
optimizer will determine that std::numeric_limits<T>::is_signed is a
compile-time constant and throw away the dead branch, but still both
templates are instantiated.

It is easy to eliminate the unnecessary instantiation and make the
compiler instantiate only the template function which is actually called:

     #include <limits>

     template<bool> struct Bool {};
     typedef Bool<true> True;
     typedef Bool<false> False;

     template<class T>
     void algo(const T& one, const T& two, /*signed*/ True) {
         // ...
     }

     template<class T>
     void algo(const T& one, const T& two, /*signed*/ False) {
         // ...
     }

     template<class T> void algo(const T& one, const T& two) {
         algo(one, two, Bool<std::numeric_limits<T>::is_signed>());
     }

     int main()
     {
         algo(1, 1);
         algo(1u, 1u);
     }

Very good, but no cigar.

Some days ago, James Kanze posted an equivalent snippet (your Bool<>
== his Discrim<>).

Sorry, the "no cigar" sentence has made my horror & delight so many
times that I _had_ to use it at least once...

Of course, I wasn't able to work out that solution by myself: one
cigar each ;-)
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top