🔒 Locked
Limiting function template types
Advertisement
Original Post
Is there any way to limit templated function template types? For example, I wrote some math functions which work with generic numerical data. char, short, int, float, double, long, signed, unsigned, etc. They only work for this type of data because some of the functions can return zero, where the return type is of the template type. Is there any way to restrict the template function to generic types? If this means throwing an error inside of the function, and that's the best I can do, then I'll take that. But I'm not even sure how I could manage that. Are most templated functions not normally designed to work with certain ranges of data types in this way? Is there an unwritten (or even written) law that states all templated functions / classes must work with all data types? Thanks for any help.
Quote:Not at all. If you perform an operation on a parameter that is not supported by some type, you will get a compile time error when you try to pass a value of that type. There is also boost.static_assert, which allows you to give your own compile time error messages.
Original post by Kest
Are most templated functions not normally designed to work with certain ranges of data types in this way? Is there an unwritten (or even written) law that states all templated functions / classes must work with all data types?
Unfortunately you can't specify within the language whether a function is parametric (universally quantified over all types) or whether is uses bounded quantification over a set of types which support some operation. "Concepts" in C++0x are designed to solve this.
The Boost Concept Check Library is meant to handle the kinds of things you mention, but it doesn't seem to include a "has numeric operators" concept (according to the Reference, anyways). There is a test for built-in integer types, but that doesn't allow for floating point types or custom types with all the appropriate operators defined.
As for returning zero, make sure you're doing T(0) where T is the type name from the template to allow for constructors declared explicit.
As for returning zero, make sure you're doing T(0) where T is the type name from the template to allow for constructors declared explicit.
You can, using a compile-time assert, but you SHOULDN'T.
You limit your template to only built-in numeric types because you figure 'that's all it will compile with anyway'. Well, you are wrong. It will compile with any type that supports those operations. If I write a class that behaves like a numeric type, and I want to use it with your template, I can't. I'd have to go modify your code to allow it. That is the exact opposite of generic programming.
Furthermore, if you want to return a 'T with the value of 0', don't rely on automatic conversions to do it. Don't write 'return 0;', write 'return T(0);'. Yes, that works with built in types just fine, and it will make your template just a little more generic.
[edit]Bleh on you extraneous. :P Beat me to the second point. :([/edit]
You limit your template to only built-in numeric types because you figure 'that's all it will compile with anyway'. Well, you are wrong. It will compile with any type that supports those operations. If I write a class that behaves like a numeric type, and I want to use it with your template, I can't. I'd have to go modify your code to allow it. That is the exact opposite of generic programming.
Furthermore, if you want to return a 'T with the value of 0', don't rely on automatic conversions to do it. Don't write 'return 0;', write 'return T(0);'. Yes, that works with built in types just fine, and it will make your template just a little more generic.
[edit]Bleh on you extraneous. :P Beat me to the second point. :([/edit]
Quote:
Original post by Kest
Is there any way to limit templated function template types? For example, I wrote some math functions which work with generic numerical data. char, short, int, float, double, long, signed, unsigned, etc. They only work for this type of data because some of the functions can return zero, where the return type is of the template type.
SFINAE to the rescue. What you want to do is have substituation fail when a certain compile-time condition is not met, in this case you want to check if the type is arithmetic. This is easily done with boost using enable_if and Boost.TypeTraits:
#include <boost/utility/enable_if.hpp>#include <boost/type_traits/is_arithmetic.hpp>template< typename ParamType >typename ::boost::enable_if< ::boost::is_arithmetic< ParamType > , ParamType > ::typeyour_function( ParamType arg ){ // Do whatever return arg;}
IIRC, boost::is_arithmetic relies on type traits. Which means, to use your template, I have to supply a specialization of is_arithmetic to confirm that yes, my type really is arithmetic, even though it's not built in.
I say again, don't do this. If it works, let me use it. If not, I'll get a compile error. No problems at all.
I say again, don't do this. If it works, let me use it. If not, I'll get a compile error. No problems at all.
Deyja: While in simple cases, such things can cause problems, with seriously templated code such things can make the code much simpler to use. Consider the example on the "Boost Concept Check Library" page.
While the STL (used in the example) is well documented, third party libraries rarely are (and it's easy leave out something important even when the docs are generally good). I'd rather have to spend a few minutes to define type traits for my custom types than spend a few days dealing with complex third-party template code trying to figure out that a function requires a certain operator.
While the STL (used in the example) is well documented, third party libraries rarely are (and it's easy leave out something important even when the docs are generally good). I'd rather have to spend a few minutes to define type traits for my custom types than spend a few days dealing with complex third-party template code trying to figure out that a function requires a certain operator.
Quote:
Original post by Deyja
IIRC, boost::is_arithmetic relies on type traits. Which means, to use your template, I have to supply a specialization of is_arithmetic to confirm that yes, my type really is arithmetic, even though it's not built in.
His requirements did not mention working for non-built-in types. In it's current form, it will allow the function to be called for all arithmetic types and the template will not be instantiatied for non-arithmetic types.
If he actually wanted to allow for other user-defined types, he wouldn't want to use is_arithmetic directly, but rather, he'd want to encapsulate its functionality and allow for specialization (specializing is_arithmetic for non-built-in types is not proper use since the term arithmetic has a fixed meaning in the standard).
Quote:
Original post by Deyja
I say again, don't do this. If it works, let me use it. If not, I'll get a compile error. No problems at all.
I have to say, I disagree with you entirely. This is precicely the type of situation that enable_if is great for. It's a simple step towards using Design-by-Contract in C++.
If you want to avoid such checks altogether, you may (or may not) still get compiler errors, however they will often be more difficult to comprehend and will occur in unintuitive locations (at the point at which an unsupported functionality is used, rather than at the call-site). Explicit checks catch errors early and can provide much more understandable messages.
Quote:
Original post by Deyja
If I write a class that behaves like a numeric type, and I want to use it with your template, I can't. I'd have to go modify your code to allow it. That is the exact opposite of generic programming.
No, you don't have to modify their code to allow it, all you have to do is make your type acknowledge that it supports the desired interface and meets its guarantees. It's not the opposite of generic programming, on the contrary, DBC is extremely powerful in generic programming.
There's no reason to have to resort to a boost static assert. I needed something similar recently at work. My solution was to temporarily 'static_cast' the data to a double. That way anything that couldn't be casted to a double would fail to compile, and any classes that could be cast to a double would be valid assuming that they overloaded all the required operations.
Quote:
Original post by Deyja
You limit your template to only built-in numeric types because you figure 'that's all it will compile with anyway'. Well, you are wrong. It will compile with any type that supports those operations. If I write a class that behaves like a numeric type, and I want to use it with your template, I can't. I'd have to go modify your code to allow it. That is the exact opposite of generic programming.
My goal wasn't really to limit the functions to generic types. I'm just concerned that some types could become unstable, invalid, or incorrect in terms of the operation of the function, if zero is set as the value. The functions were not specifically written for generic types, but they were specifically written for numerical types. And it's entirely possible to send non-numerical types to the function, have it compile and even execute without error, and give back incorrect results. With a custom string type, for example. Or a vector or matrix. There's very few cases where a zeroed out matrix is the answer to anything.
My goal was to write generic functions for dealing with numbers, while making it obvious that they are intended only for numbers. Some sugar coating would be to also make it impossible to accidently use them with anything else. It's not crucial. It would take a dumb mistake to attempt to use them incorrectly. It's just unfortunate because I've made plenty of dumb mistakes in the past.
Thanks to everyone for all of the information [smile]
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement