[c++] Explicit specialization of a template.

Z

ZikO

Hi

there's the simple template of the function which converts a number into
bits as below:

template<class T>
const std::string toBits(const T& n, const char& sep = '\0') {
size_t no_bits = sizeof(n)*8;
std::string bits;
for(size_t shift = no_bits; shift > 0; shift--) {
char ch = ((n>>(shift-1)) & 1) ? '1' : '0';
bits.push_back(ch);
if((no_bits > 8) && (((shift-1) % 8) == 0)) {
if(sep != '\0')
bits.push_back(sep);
}
}
return bits;
}

Now. I don't want the function to operate on double / float / long
double / bool etc. I've supplied the code with explicit specializations:

template<>
const std::string toBits<double>(const double& n, const char& sep = '\0') {
return string("It is not allowed to use bit operands on double type");

4 times for all four floating types. I cannot compile the code. The
compiler says for every specialization the same error:

error: default argument specified in explicit specialization




The second thing is I don't know how I could simply eliminate any other
types which is not char int or its derivative like short long etc. let's
say if someone try to pass object of X class I would like compiler to
use general specialization of the function which stays it's wrong type.

Regards.
 
I

Ian Collins

ZikO said:
Hi

there's the simple template of the function which converts a number into
bits as below:

template<class T>
const std::string toBits(const T& n, const char& sep = '\0')

Now. I don't want the function to operate on double / float / long
double / bool etc. I've supplied the code with explicit specializations:

template<>
const std::string toBits<double>(const double& n, const char& sep = '\0') {
return string("It is not allowed to use bit operands on double type");

4 times for all four floating types. I cannot compile the code. The
compiler says for every specialization the same error:

error: default argument specified in explicit specialization
That's write, you can't. Just write

template<>
const std::string toBits(const double& n, const char& ) {
return std::string("It is not allowed to use bit operands on double
type");
}
Why the the second parameter a const reference?
The second thing is I don't know how I could simply eliminate any other
types which is not char int or its derivative like short long etc. let's
say if someone try to pass object of X class I would like compiler to
use general specialization of the function which stays it's wrong type.

Not using specialisation. You could include an assignment to the
largest supported integral type in the function which would fail to
compile for types that can't be converted.
 
I

Ian Collins

Ian said:
That's write, you can't.

I wasn't very clear there, I should have said "that's right, you can't
change an existing default argument". The function already has a
default argument.
 
S

szymonwlodarski

Now. I don't want the function to operate on double / float / long
double / bool etc. (...)

They will not (except for the bool). The function will work only, if
you
pass to it type which has either built-in or overloaded
(appropriately)
operator >>. Conveniently, you do not have to do anything - it works
fine
as it is.
However, you can achieve this with:

const std::string toBits( double, char = '\0' ) {
...
}

const std::string toBits( float, char = '\0' ) {
...
}

template<class T>
const std::string toBits(const T& n, const char& sep = '\0') {
...
}
 
I

Ian Collins

ZikO said:
Hi

there's the simple template of the function which converts a number into
bits as below:

template<class T>
const std::string toBits(const T& n, const char& sep = '\0') {
size_t no_bits = sizeof(n)*8;
std::string bits;
for(size_t shift = no_bits; shift > 0; shift--) {
char ch = ((n>>(shift-1)) & 1) ? '1' : '0';
bits.push_back(ch);
if((no_bits > 8) && (((shift-1) % 8) == 0)) {
if(sep != '\0')
bits.push_back(sep);
}
}
return bits;
}

This looked like an interesting case for metaprogramming, so I cooked up
this (I'll leave adding the separator as an exercise for the reader!):

template <unsigned N> struct Unsigned
{ static const unsigned value = N; };

template <typename T, typename N> void
nextBit( T n, char* bits, N )
{
*bits++ = (n>>N::value)&1 ? '1' : '0';
nextBit( n, bits, Unsigned<N::value-1>() );
}

template<typename T> void
nextBit( T n, char* bits, Unsigned<0> )
{
*bits++ = n&1 ? '1' : '0';
}

template <class T> std::string
toBits( T n )
{
char tmp[CHAR_BIT*sizeof(T)+1];

nextBit( n, tmp, Unsigned<CHAR_BIT*sizeof(T)-1>() );

tmp[CHAR_BIT*sizeof(T)] = '\0';

return tmp;
}

Depending on how aggressive your compiler's optimiser is, you could get
some interesting results. For example:

std::string a(toBits( 'z' ));

compiled to:

leal -40(%ebp),%ecx ;/ line : 30
movb $48,-40(%ebp) ;/ line : 30
movb $49,-39(%ebp) ;/ line : 30
movb $49,-38(%ebp) ;/ line : 30
movb $49,-37(%ebp) ;/ line : 30
movb $49,-36(%ebp) ;/ line : 30
movb $48,-35(%ebp) ;/ line : 30
movb $49,-34(%ebp) ;/ line : 30
movb $48,-33(%ebp) ;/ line : 31
movb $0,-32(%ebp) ;/ line : 47

With Sun CC.
 
Z

ZikO

ZikO said:
template<>
const std::string toBits<double>(const double& n, const char& sep = '\0') {
return string("It is not allowed to use bit operands on double type");

I found the problem. As u can see second parameter is defining by
default value '\0'. After removing this, everything works perfectly.

template<>
const std::string toBits<double>(const double& n, const char& sep)
{
return string("It is not allowed to use bit operands on double type");
}

Thanks for all suggestions.

Regards.
 
I

Ian Collins

ZikO said:
I found the problem. As u can see second parameter is defining by
default value '\0'. After removing this, everything works perfectly.

template<>
const std::string toBits<double>(const double& n, const char& sep)
{
return string("It is not allowed to use bit operands on double type");
}

This still doesn't explain why you
a) return a const string and
b) why the sep parameter is a const char&.
 
Z

ZikO

Ian said:
This still doesn't explain why you
a) return a const string and
b) why the sep parameter is a const char&.

If you want to suggest something, just state that clearly. It is no
point checking my knowledge here or waiting for answers you probably
know already. You know I am newbie here but i will try to answer.

ASAIK const prevent from keeping value returned from function as
l-value. It is nonsens to keep this function as l-value so cont prevent
from doing this.

2 parameters are passed by reference with const because it is more
effective with larger objects and is the result of my habbit, which I
believe is a good habbit. I avoid copying temporary objects here. As I
said it might make more sens with larger objects.

Happy?
 
I

Ian Collins

ZikO said:
If you want to suggest something, just state that clearly. It is no
point checking my knowledge here or waiting for answers you probably
know already. You know I am newbie here but i will try to answer.

No offence intended, I was curious. The question was intended to make
you think about your design, which you obviously had.
ASAIK const prevent from keeping value returned from function as
l-value. It is nonsens to keep this function as l-value so cont prevent
from doing this.

Fair enough although it does appear a bit restrictive.
2 parameters are passed by reference with const because it is more
effective with larger objects and is the result of my habbit, which I
believe is a good habbit. I avoid copying temporary objects here. As I
said it might make more sens with larger objects.

It does make sense with larger objects, but certainly not a char. Looks
like premature anti-optimisation to me!

No, it's too hot here...
 
J

James Kanze

ZikO wrote:
That's write, you can't. Just write

That's right, you can't:).
template<>
const std::string toBits(const double& n, const char& ) {
return std::string("It is not allowed to use bit operands on double
type");}
Why the the second parameter a const reference?
Not using specialisation. You could include an assignment to
the largest supported integral type in the function which
would fail to compile for types that can't be converted.

Except that that might prevent instantiation on user defined
types which behave like integers.

I'm not sure I see the problem. Without the specialization, he
should get a compile time error if he instantiates the function
over a double, because he uses the shift operator in the
function. His function can only be instantiated over types for
which the shift operator is legal. If he wants a better error
message, he should be able to use something along the lines of:

int instantiation_type_must_be_integer[
std::numeric_limits< T >::is_integer ] ;

If he really does want to defer the error until runtime, of
course, he can have the function call an overloaded function:

template< bool b > struct Discrim {} ;

template< typename T >
std::string
toBits(
T n,
Discrim< false > )
{
return "some error message" ;
}

template< typename T >
std::string
toBits(
T n,
Discrim< true > )
{
char result[ sizeof( n ) * CHAR_BIT ] ;
char* dest = result + sizeof( result ) ;
while ( dest != result ) {
*dest = "01"[ n & 1 ] ;
n >>= 1 ;
-- dest ;
}
return std::string( result, result + sizeof( result ) ) ;
}

template< typename T >
std::string
toBits(
T n )
{
return toBits(
n,
Discrim< numeric_limits< T >::is_integer >() ) ;
}

Or he could drop the error entirely, and support dumping the
underlying bit pattern of anything, by reinterpret_cast'ing the
address of the object to unsigned char*:

template< typename T >
std::string
toBits(
T const& n )
{
char result[ sizeof( n ) * CHAR_BIT ] ;
char* dest = result + sizeof( result ) ;
unsigned char const*p
= reinterpret_cast< unsigned char const* >( &n + 1 ) ;
while ( p != reinterpret_cast< unsigned char const* >( &n ) )
{
-- p ;
unsigned char byte = *p ;
for ( std::size_t count = CHAR_BIT ; count != 0 ; --
count ) {
*dest = "01"[ byte & 1 ] ;
byte >>= 1 ;
-- dest ;
}
}
return std::string( result, result + sizeof( result ) ) ;
}
 
J

James Kanze

If you want to suggest something, just state that clearly. It
is no point checking my knowledge here or waiting for answers
you probably know already. You know I am newbie here but i
will try to answer.

I don't think he was being aggressive about it.
ASAIK const prevent from keeping value returned from function
as l-value. It is nonsens to keep this function as l-value so
cont prevent from doing this.

Const and lvalue-ness are orthogonal. A return value of a
funcntion is never an lvalue.

What const does in the return value is prevent the user from
calling non-const functions on it. Usually, this is not
important, and not worth the bother (although there are
exceptions). In the case of std::string, returning a const
prevents chaining, e.g things like:

std::string s = toBits( x ).append( " bits " ) ;

When a class supports chaining, as std::string does, it's
probably a bad idea to return a const.
2 parameters are passed by reference with const because it is
more effective with larger objects and is the result of my
habbit, which I believe is a good habbit. I avoid copying
temporary objects here. As I said it might make more sens with
larger objects.

There are two widespread conventions: pass by value until the
profiler says otherwise, and (much more widespred) pass
non-class types by value, class types by const reference.
Passing everything by const reference is counter-productive, at
least if performance is an issue; it's often cheaper to copy the
object than to use the additional indirection that const
references imply in practice.
 
I

Ivan

The second thing is I don't know how I could simply eliminate any other
types which is not char int or its derivative like short long etc. let's
say if someone try to pass object of X class I would like compiler to
use general specialization of the function which stays it's wrong type.

In fact, this is a way to implement your requirement, but not very
good.

//define a class template some like mate-function.

template< class T>
struct Integer
{
};

//provide specializations for types such as char, int, short and long,
etc.
template <>
struct Integer<int>
{
typedef int type;
}
....char, short, long, etc...

Then write the function with below signature:

template<class T>
const std::string toBits(const T& n, const char& sep = '\0', typename
Integer<T>::type dummy = 0) { ...}

So the function has not instantiation with types but int, char, short
and long, etc. Because Integer<string> has no a nested type name
called "type", it is not consistent with the third type of function.
But this method introduce a useless parameter.

The typical way to implement some like above, you can use the
enable_if and disable_if utilities of boost. It provide the simplest
way to do type checking for function template.
 

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,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top