views:

117

answers:

5

I have a bunch of overloaded functions that operate on certain data types such as int, double and strings. Most of these functions perform the same action, where only a specific set of data types are allowed. That means I cannot create a simple generic template function as I lose type safety (and potentially incurring a run-time problem for validation within the function).

Is it possible to create a "semi-generic compile time type safe function"? If so, how? If not, is this something that will come up in C++0x?

An (non-valid) idea;

template <typename T, restrict: int, std::string >
void foo(T bar);
...
foo((int)0); // OK
foo((std::string)"foobar"); // OK
foo((double)0.0); // Compile Error

Note: I realize I could create a class that has overloaded constructors and assignment operators and pass a variable of that class instead to the function.

+1  A: 

You may create a "private" templatized function that is never exposed to the outside, and call it from your "safe" overloads.

By the way, usually there's the problem with exposing directly the templatized version: if the passed type isn't ok for it, a compilation error will be issued (unless you know your algorithm may expose subtle bugs with some data types).

Matteo Italia
You should take into account implicit conversions. Careful if you have an overload `foo(double)` but want to disable it for all integer types etc.
UncleBens
Not really helpful since I still need to keep those public functions present..
Fredrik Ullner
@UncleBens: yes, you're right. @Fredrik Ullner: what's the problem? Just create the template version only in the .cpp (calling it in a slightly different manner), and leave only the overloads in the .hpp (or .h) to be used from other compilation units.
Matteo Italia
A: 

Perhaps a bit ugly solution, but functors could be an option:

class foo {
   void operator()(double); // disable double type
public:
   template<typename T>
   void operator ()(T bar) {
      // do something
   }
};

void test() {
   foo()(3); // compiles
   foo()(2.3); // error
}

Edit: I inversed my solution

class foo {
   template<typename T>
   void operator ()(T bar, void* dummy) {
     // do something
   }
public:
   // `int` is allowed
   void operator ()(int i) {
      operator ()(i, 0);
   }
};

foo()(2.3); // unfortunately, compiles
foo()(3); // compiles
foo()("hi"); // error
Nick D
Interesting idea, but that would mean one would need to think out all potential data types that are invalid... Of which there are a (practical) infinite number..
Fredrik Ullner
@Fredrik, yes that's a problem. I updated my answer.
Nick D
A: 

You could probably work with templates specializations for the "restricted" types you want to allow. For all other types, you don't provide a template specialization so the generic "basic" template would be used. There you could use something like BOOST_STATIC_ASSERT to throw a compile error.

Here some pseudo-code to clarify my idea:

template <typename T>
void foo(T bar) {BOOST_STATIC_ASSERT(FALSE);}

template<> // specialized for double
void foo(double bar) {do_something_useful(bar);};
nabulke
The correct syntax is a non-template overload. :)
UncleBens
I can't find the proper documentation for the "typename: specialized for double"... Anyone?
Fredrik Ullner
`template<> void foo<double>(double bar);` But it is recommended not to specialize template functions: `void foo(double bar);`
UncleBens
The edited code require overloads of foo (for all the types), which the intention is to remove.
Fredrik Ullner
@UncleBens - Thanks for your comments, if I think about it, providing specializations for template functions is kind of paradox. But it's ok for templates classes I guess...At least I did learn something :-)
nabulke
+4  A: 

Use sfinae

template<typename> struct restrict { };
template<> struct restrict<string> { typedef void type; };
template<> struct restrict<int> { typedef void type; };

template <typename T>
typename restrict<T>::type foo(T bar);

That foo will only be able to accept string or int for T. No hard compile time error occurs if you call foo(0.f), but rather if there is another function that accepts the argument, that one is taken instead.

Johannes Schaub - litb
Posted code does not even compile in VC++9. ("too few template arguments" on second line [among other errors])
Fredrik Ullner
@fredrik, fixed
Johannes Schaub - litb
I can't edit your code, but adding template <> before "struct restrict..." makes this work. Template vodoo..
Fredrik Ullner
Oops, i'm sorry i was asleep when i wrote that.
Johannes Schaub - litb
A: 

To list an arbitrary selection of types I suppose you could use a typelist. E.g see the last part of my earlier answer.

The usage might be something like:

//TODO: enhance typelist declarations to hide the recursiveness
typedef t_list<std::string, t_list<int> > good_for_foo; 

template <class T>
typename boost::enable_if<in_type_list<T, good_for_foo> >::type foo(T t);
UncleBens