views:

199

answers:

5

Let's assume we have a template function "foo":

template<class T>
void foo(T arg)
{ ... }

I can make specialization for some particular type, e.g.

template<>
void foo(int arg)
{ ... }

If I wanted to use the same specialization for all builtin numeric types (int, float, double etc.) I would write those lines many times. I know that body can be thrown out to another function and just call of this is to be made in every specialization's body, however it would be nicer if i could avoid writting this "void foo(..." for every type. Is there any possibility to tell the compiler that I want to use this specialization for all this types?

A: 

maybe you can define a default template function that will work on all native type, and delegate specialization of custom type to user

YeenFei
+8  A: 

You could use std::numeric_limits to see whether a type is a numeric type (is_specialized is true for all float and integer fundamental types).

// small utility
template<bool> struct bool2type { };

// numeric
template<typename T>
void fooImpl(T arg, bool2type<true>) {

}

// not numeric
template<typename T>
void fooImpl(T arg, bool2type<false>) {

}

template<class T>
void foo(T arg)
{ fooImpl(arg, bool2type<std::numeric_limits<T>::is_specialized>()); }
Johannes Schaub - litb
Neat idea! But why not just make `fooImpl` depend on one type and one boolean argument?
Vlad
@Vlad because we want compile time branching. If we pass a `bool` function argument, we will have the code executed in the same function and use `if` to branch. That will unfortunately type-check both branches, and very likely miss the goal.
Johannes Schaub - litb
@Johannes: I meant two template arguments, just to spare `bool2type` definition.
Vlad
@Vlad, we can't do that because function templates cannot be partially specialized.
Johannes Schaub - litb
@Johannes: I see, thanks!
Vlad
This will work as long as you do not specialize `std:: numeric_limits` for custom type. I'd use `boost::is_fundamental`.
Kirill V. Lyadvinsky
boost::is_fundamental includes void; you want is_arithmetic; or, if you have access to std::tr1, there is std::tr1::is_arithmetic. Both have many other similar type traits.
Patrick Johnmeyer
@Kirill, right you could specialize for a custom type. But if it is a numeric custom type, it could fit the bill too. But indeed if you really only want the fundamental numeric types, a dedicated template looks better.
Johannes Schaub - litb
+1  A: 

You can use an approach with preprocessor.

foo.inc:

template<>
void foo(TYPE arg)
{ /* do something for int, double, etc. */ }

foo.h:

template<class T>
void foo(T arg)
{ /*do something */ }

#define TYPE int
#include "foo.inc"
#undef TYPE

#define TYPE double
#include "foo.inc"
#undef TYPE

etc.

Vlad
+1  A: 

With boost:

#include <boost/type_traits/is_scalar.hpp>
#include <iostream>
#include <string>

namespace detail
{
    typedef const boost::true_type& true_tag;
    typedef const boost::false_type& false_tag;

    template <typename T>
    void foo(const T& pX, true_tag)
    {
        std::cout << "special: " << pX << std::endl;
    }

    template <typename T>
    void foo(const T& pX, false_tag)
    {
        std::cout << "generic: " << pX << std::endl;
    }
}

template <typename T>
void foo(const T& pX)
{
    detail::foo(pX, boost::is_scalar<T>());
}

int main()
{
    std::string s = ":D";
    foo(s);
    foo(5);
}

You can mostly easily do it without boost:

#include <iostream>
#include <string>

// boolean stuff
template <bool B>
struct bool_type {};

typedef bool_type<true> true_type;
typedef bool_type<false> false_type;

// trait stuff
template <typename T>
struct is_scalar : false_type
{
    static const bool value = false;
};

#define IS_SCALAR(x) template <> \
            struct is_scalar<x> : true_type \
            { \
                static const bool value = true; \
            };

IS_SCALAR(int)
IS_SCALAR(unsigned)
IS_SCALAR(float)
IS_SCALAR(double)
// and so on

namespace detail
{
    typedef const true_type& true_tag;
    typedef const false_type& false_tag;

    template <typename T>
    void foo(const T& pX, true_tag)
    {
        std::cout << "special: " << pX << std::endl;
    }

    template <typename T>
    void foo(const T& pX, false_tag)
    {
        std::cout << "generic: " << pX << std::endl;
    }
}

template <typename T>
void foo(const T& pX)
{
    detail::foo(pX, is_scalar<T>());
}

int main()
{
    std::string s = ":D";
    foo(s);
    foo(5);
}
GMan
A: 

You could write a small script (e.g., Perl) to generate the source file for you. Create an array containing all the types you want to specialize, and have it write out the function header for each. You can even embed the script execution in your makefile to automagically re-run it if you change something.

Note: this assumes the implementation of foo can be made trivial and similar for each type, e.g., simply calling out to the real implementation function. But it avoids a bunch of template/preprocessor mumbo-jumbo that might make a future maintainer scratch his head.

Kristo