Template magic needed, partial specialization

Discussion in 'C++' started by Niklas Norrthon, Dec 6, 2005.

  1. 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
     
    Niklas Norrthon, Dec 6, 2005
    #1
    1. Advertising

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

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Dec 6, 2005
    #2
    1. Advertising

  3. (Alf P. Steinbach) writes:

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


    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
    template<> sometimes, and when I need to specify types with
    typename.

    > > 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
     
    Niklas Norrthon, Dec 7, 2005
    #3
  4. Niklas Norrthon <> writes:

    > 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
     
    Niklas Norrthon, Dec 7, 2005
    #4
  5. * Niklas Norrthon:
    > * Alf P. Steinbach:
    >
    > >
    > > 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.


    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.


    > > > /* 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?


    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

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
     
    Alf P. Steinbach, Dec 7, 2005
    #5
  6. (Alf P. Steinbach) writes:

    > 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
     
    Niklas Norrthon, Dec 7, 2005
    #6
    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. Chandra Shekhar Kumar

    template partial specialization

    Chandra Shekhar Kumar, Jun 24, 2003, in forum: C++
    Replies:
    17
    Views:
    3,060
    tom_usenet
    Jun 25, 2003
  2. Joseph Turian
    Replies:
    2
    Views:
    492
  3. toton
    Replies:
    1
    Views:
    630
  4. vj
    Replies:
    1
    Views:
    505
  5. Hizo
    Replies:
    17
    Views:
    714
    itaj sherman
    Mar 7, 2011
Loading...

Share This Page