Template magic needed, partial specialization

  • Thread starter Niklas Norrthon
  • Start date
N

Niklas Norrthon

I've been banging my head in the wall for some time now over a
little problem having to do with partial specialization of
function templates.

The real problem is more complex than this, but after trimming
out irrelevant stuff, this is what remains. The problem is to
have a function template with different specialization for
signed an unsigned template parameters. Lets assume for now that
I want to test signed arguments for negative values and do
something special with these cases, while unsigned arguments
should not be tested at all.

Consider the following code:

#include <iostream>
#include <limits>

/*
* Helper function called by f<IntType>.
*/
template <typename IntType, bool is_signed> void g(IntType x);

/* Now I want to have two specializations of the above: */
template <typename IntType>
void g<IntType, true>(IntType x) /* Signed types end up here */
{
if (x < 0) { /* ok, x is signed */
/* Special logic here */
}
}

template <typename IntType>
void g<IntType, false>(IntType)
{
/* x is unsigned, so don't have to do anything */
}

/*
* Function called by main. Only template parameter
* is the IntType.
*/
template <typename IntType>
void
f(IntType x)
{
#if 0
/* This would result in compiler warnings if IntType is unsigned: */
if (numeric_limits<IntType>::is_signed && x < 0) {
/* special logic here */
}
#else
/* So let's try this instead */
g<IntType, numeric_limits<IntType>::is_signed>(x);

#endif
/* More logic here */
}

int
main()
{
unsigned char uc = -1;
unsigned short us = -1;
unsigned int ui = -1;
unsigned long ul = -1L;
signed char sc = -1;
signed short ss = -1;
signed int si = -1;
signed long sl = -1L;

f(uc);
f(us);
f(ui);
f(ul);

f(sc);
f(ss);
f(si);
f(sl);

return 0;
}

The compiler complains with the following message:
foo.cc:11: error: function template partial specialization
'g<IntType, true>' is not allowed

I'd appreciate some hints how to get on.

When this is solved I'll have to deal with right shifting:

typedef unsigned short MyInt;
const MyInt my_int_mask = -1;

template <typename UnsignedIntType>
void f(UnsignedIntType x)
{

while (x > 0) {
MyInt y = x /* & my_int_mask */;
/* do something with y */
/* next line does not work if sizeof y >= sizeof x */
x >>= numeric_limits<MyInt>::digits; /* more template magic needed */
}
}

/Niklas Norrthon
 
A

Alf P. Steinbach

* Niklas Norrthon:
I've been banging my head in the wall for some time now over a
little problem having to do with partial specialization of
function templates.

You're aware that there's no such?

The real problem is more complex than this, but after trimming
out irrelevant stuff, this is what remains. The problem is to
have a function template with different specialization for
signed an unsigned template parameters. Lets assume for now that
I want to test signed arguments for negative values and do
something special with these cases, while unsigned arguments
should not be tested at all.

To detect signedness at compile time you can do something like

template< typename T >
struct IsSigned
{
enum{ yes = (T(-1) < 0), no = !yes };
};

int main()
{
std::cout << IsSigned<unsigned>::yes << std::endl;
std::cout << IsSigned<signed>::yes << std::endl;
}

Consider the following code:
[snip]
template <typename IntType>
void
f(IntType x)
{
#if 0
/* This would result in compiler warnings if IntType is unsigned: */
if (numeric_limits<IntType>::is_signed && x < 0) {
/* special logic here */
}
#else
/* So let's try this instead */
g<IntType, numeric_limits<IntType>::is_signed>(x);

You can't use the result of a run-time call at compile time.


[snip]
I'd appreciate some hints how to get on.

The best is to write code that's signedness-agnostic.

Failing that, you can use the technique shown above.
 
N

Niklas Norrthon

* Niklas Norrthon:

You're aware that there's no such?

Now I am, after digging out the standard and looking for it, and
some googling of the archives. I also became aware of that I'm
not the only one missing partial specialization of function
templates, and I see no reason why it couldn't be included in
the language, so I hope it'll find it way in eventually.
To detect signedness at compile time you can do something like

template< typename T >
struct IsSigned
{
enum{ yes = (T(-1) < 0), no = !yes };
};

int main()
{
std::cout << IsSigned<unsigned>::yes << std::endl;
std::cout << IsSigned<signed>::yes << std::endl;
}

Which leaves me with exactly the same compiler warning I wanted to
get rid of in the first place.

I admit it's no big deal. The compiler warns me that comparing
an unsigned to less than zero will always result in false. The
problem is that it is in a template, so I will get the warning
for every type I instanciate the template with, and each warning
is three or four lines, since it also tells me where the
template is instanciated and so on, and the code lives in a
header file, so I get it for every file including the header,
which in the end leaves me with pages of useless warnings.

It also gave me the opportunity to dig into some pretty advanced
template hacking, which is something I don't do every day, and
I have learned (I think) when and why I need to say
Consider the following code:
[snip]
template <typename IntType>
void
f(IntType x)
{
#if 0
/* This would result in compiler warnings if IntType is unsigned: */
if (numeric_limits<IntType>::is_signed && x < 0) {
/* special logic here */
}
#else
/* So let's try this instead */
g<IntType, numeric_limits<IntType>::is_signed>(x);

You can't use the result of a run-time call at compile time.

What are you talking about?
IntType is the template argument from above, known at compile time.
numeric_limits<IntType>::is_signed is a static constant bool
defined in the header <limits> for all built in types, and also
known at compile time. The above would expand to one of:

g<unsigned char, false>(x);
g<signed char, true>(x);
g<char, true>(x);
g<char, false>(x);
g<unsigned short, false>(x);
g<signed short, true>(x);
g<unsigned int, false>(x);
g<signed int, true>(x);
g<unsigned long, false>(x);
g<signed long, true>(x);

Or some other user defined type for which numeric_limits is specialized.
[snip]
I'd appreciate some hints how to get on.

The best is to write code that's signedness-agnostic.

Failing that, you can use the technique shown above.

Failing that, the outcommented code works fine. I just wanted to
get rid of loads of warnings, and learning some advanced
template programming. I did the latter but not the former...

/Niklas Norrthon
 
N

Niklas Norrthon

Niklas Norrthon said:
I've been banging my head in the wall for some time now over a
little problem having to do with partial specialization of
function templates.

Finally I found a solution to my problem, that I think is
useful enough to share.

/*
* Instead of partial specialization of the function
* templates, first I introduce a dummy template class
* with two possible specializations:
*/

template <bool is_signed>
struct IsSigned { };

/*
* Then I define two versions of the function where the
* actual logic resides. They are overloaded with the
* dummy class above:
*/

/* signed version: */
template <typename IntType>
void g(IntType x, Signed<true> /* dummy arg */)
{
if (x < 0) {
/* special logic here */
}
}

/* Unsigned version */
template <typename IntType>
void g(IntType x, Signed<false> /* dummy arg */)
{
/* do nothing, or do special unsigned logic here */
}

/*
* Finally I have the one parameter version, which
* is called from outside:
*/

/* Common version */
template <typename IntType>
void g(IntType x)
{
/* First special treatment depending on signedness: */
g(x, Signed<numeric_limits<IntType>::is_signed>());

/* Then common logic to both signed and unsigned types */
}

/* Finally my main unchanged from before: */
int
main()
{
unsigned char uc = -1;
unsigned short us = -1;
unsigned int ui = -1;
unsigned long ul = -1L;
signed char sc = -1;
signed short ss = -1;
signed int si = -1;
signed long sl = -1L;

f(uc);
f(us);
f(ui);
f(ul);

f(sc);
f(ss);
f(si);
f(sl);

return 0;
}

/Niklas Norrthon
 
A

Alf P. Steinbach

* Niklas Norrthon:
* Alf P. Steinbach:


Which leaves me with exactly the same compiler warning I wanted to
get rid of in the first place.

If that warning is due to your having no partial specialization,
consider the simple trick of putting your function in a class. Classes
can be partially specialized. Another trick is to use function
overloading: you can't partially specialize a function, but you can
overload it, so in this case transform the boolean value to a type, one
for true and one for false, and use a dummy argument of that type.

What are you talking about?

I'm very sorry, bowing head in shame and applying the nearest wall to my
forehead: it was a parse error, on my part, and furthermore there was no
need for my IsSigned class (except it's usually faster to just code such
a thing than read the docs...).


[snip]
Failing that, the outcommented code works fine. I just wanted to
get rid of loads of warnings, and learning some advanced
template programming. I did the latter but not the former...

Hope the above helps with both, then,

- Alf
 
N

Niklas Norrthon

If that warning is due to your having no partial specialization,
consider the simple trick of putting your function in a class. Classes
can be partially specialized. Another trick is to use function
overloading: you can't partially specialize a function, but you can
overload it, so in this case transform the boolean value to a type, one
for true and one for false, and use a dummy argument of that type.

This is exactly what I ended up with, see my other post. Partial
specializations of function templates would have been a little bit
more elegant IMHO, but overloading with a dummy argument works, so
for the time being that is what I'll do.

Still hope for partial specializations in the future though, and I
see no reason not to include them in the language sooner or later.
Hope the above helps with both, then,

Thanks for your comments, problem finally solved. (Also the
right_shift part which a mentioned in my original post, which
was trivial in the end).

/Niklas Norrthon
 

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,969
Messages
2,570,161
Members
46,710
Latest member
bernietqt

Latest Threads

Top