[c++] Explicit specialization of a template.

Discussion in 'C++' started by ZikO, Feb 8, 2009.

  1. ZikO

    ZikO Guest

    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.
     
    ZikO, Feb 8, 2009
    #1
    1. Advertising

  2. ZikO

    Ian Collins Guest

    ZikO wrote:
    > 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')


    <snip>

    > 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.

    --
    Ian Collins
     
    Ian Collins, Feb 8, 2009
    #2
    1. Advertising

  3. ZikO

    Ian Collins Guest

    Ian Collins wrote:
    > ZikO wrote:
    >> 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')

    >
    > <snip>
    >
    >> 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.


    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.

    --
    Ian Collins
     
    Ian Collins, Feb 8, 2009
    #3
  4. ZikO

    Guest

    Re: Explicit specialization of a template.

    On 8 Lut, 18:09, ZikO <> wrote:

    > 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') {
    ...
    }

    --
    Szymon
     
    , Feb 8, 2009
    #4
  5. ZikO

    Ian Collins Guest

    ZikO wrote:
    > 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.

    --
    Ian Collins
     
    Ian Collins, Feb 8, 2009
    #5
  6. ZikO

    ZikO Guest

    ZikO wrote:
    > 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.
     
    ZikO, Feb 8, 2009
    #6
  7. ZikO

    Ian Collins Guest

    ZikO wrote:
    > ZikO wrote:
    >> 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");
    > }


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

    --
    Ian Collins
     
    Ian Collins, Feb 8, 2009
    #7
  8. ZikO

    ZikO Guest

    Ian Collins wrote:
    > 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?
     
    ZikO, Feb 9, 2009
    #8
  9. ZikO

    Ian Collins Guest

    ZikO wrote:
    > Ian Collins wrote:
    >> 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.


    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!

    > Happy?


    No, it's too hot here...

    --
    Ian Collins
     
    Ian Collins, Feb 9, 2009
    #9
  10. ZikO

    ZikO Guest

    Thanks Ian.
     
    ZikO, Feb 9, 2009
    #10
  11. ZikO

    James Kanze Guest

    Re: Explicit specialization of a template.

    On Feb 8, 8:08 pm, Ian Collins <> wrote:
    > ZikO wrote:


    > > 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')


    > <snip>


    > > 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


    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?


    > > 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.


    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 ) ) ;
    }

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Feb 9, 2009
    #11
  12. ZikO

    James Kanze Guest

    Re: Explicit specialization of a template.

    On Feb 9, 3:51 am, ZikO <> wrote:
    > Ian Collins wrote:
    > > 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.


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Feb 9, 2009
    #12
  13. ZikO

    Ivan Guest

    Re: Explicit specialization of a template.


    > 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.
     
    Ivan, Feb 13, 2009
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. J.T. Conklin
    Replies:
    1
    Views:
    442
    David Hilsee
    Aug 11, 2004
  2. Patrick Kowalzick
    Replies:
    0
    Views:
    818
    Patrick Kowalzick
    Oct 29, 2004
  3. Andy
    Replies:
    5
    Views:
    508
    Shezan Baig
    Jan 30, 2005
  4. stain
    Replies:
    3
    Views:
    957
    stain
    May 30, 2006
  5. Barry
    Replies:
    2
    Views:
    363
    Barry
    Sep 30, 2007
Loading...

Share This Page