Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Forcing all function template parameters to deduce the same type (http://www.velocityreviews.com/forums/t454693-forcing-all-function-template-parameters-to-deduce-the-same-type.html)

Dan Krantz 06-12-2006 11:33 PM

Forcing all function template parameters to deduce the same type
 
I have the following template to ensure that a given number (val) falls into
a range (between vmin & vmax):

template<typename T> T ForceNumericRange( const T& val, const T& vmin, const
T& vmax)
{
T retVal = val;

if ( retVal < vmin )
retVal = vmin;
else if ( retVal > vmax )
retVal = vmax;

return retVal;
}

Here's the call:

float comm;
....
comm = ForceNumericRange( comm, 0.0, 100.0);

The compiler (Visual Age C++ 5 under AIX 4.3) complains: The function
template parameter "T" has been deduced to have two values: "float" and
"double"

When I took the .0 off the constant parameters, it complained about T being
float & int. I solved the problem with this change:

comm = ForceNumericRange( comm, 0.0F, 100.0F);

but it's hokey, since I don't want the caller to have to explicitly specify
the type for the range constants.

Is there a way to declare the template such that the types of the 2nd and
3rd parameter are always deduced from the type of the first argument? I'm
pretty sure I could give parameters 2 & 3 a different type declaration, and
rely on runtime conversions between the different types, but I'd like this
resolved at compile time; also, that approach might fail if vmin was a
variable and vmax was a constant.

Any suggestions?



Steve Pope 06-12-2006 11:49 PM

Re: Forcing all function template parameters to deduce the same type
 
Dan Krantz <dkrantz@dakran.com> wrote:

>I have the following template to ensure that a given number (val) falls into
>a range (between vmin & vmax):


>template<typename T> T ForceNumericRange( const T& val, const T& vmin, const
>T& vmax)
>{
> T retVal = val;
>
> if ( retVal < vmin )
> retVal = vmin;
> else if ( retVal > vmax )
> retVal = vmax;
>
> return retVal;
>}
>
>Here's the call:
>
>float comm;
>...
>comm = ForceNumericRange( comm, 0.0, 100.0);


>The compiler (Visual Age C++ 5 under AIX 4.3) complains: The function
>template parameter "T" has been deduced to have two values: "float" and
>"double"


>When I took the .0 off the constant parameters, it complained about T being
>float & int. I solved the problem with this change:
>
>comm = ForceNumericRange( comm, 0.0F, 100.0F);
>
>but it's hokey, since I don't want the caller to have to explicitly specify
>the type for the range constants.


>Is there a way to declare the template such that the types of the 2nd and
>3rd parameter are always deduced from the type of the first argument?


Not that I can think of.

My two comments, neither of which answers your question, is that it is
uncommon to want to use float instead of double, except when
doing I/O or packing data or similar; and that using const

Steve
to define constants, instead of placing literals in function
arguments, is useful.

Howard Hinnant 06-13-2006 02:21 PM

Re: Forcing all function template parameters to deduce the same type
 
In article <e6ktl2$ebj$1@paola.aioe.org>,
"Dan Krantz" <dkrantz@dakran.com> wrote:

> I have the following template to ensure that a given number (val) falls into
> a range (between vmin & vmax):
>
> template<typename T> T ForceNumericRange( const T& val, const T& vmin, const
> T& vmax)
> {
> T retVal = val;
>
> if ( retVal < vmin )
> retVal = vmin;
> else if ( retVal > vmax )
> retVal = vmax;
>
> return retVal;
> }
>
> Here's the call:
>
> float comm;
> ...
> comm = ForceNumericRange( comm, 0.0, 100.0);
>
> The compiler (Visual Age C++ 5 under AIX 4.3) complains: The function
> template parameter "T" has been deduced to have two values: "float" and
> "double"
>
> When I took the .0 off the constant parameters, it complained about T being
> float & int. I solved the problem with this change:
>
> comm = ForceNumericRange( comm, 0.0F, 100.0F);
>
> but it's hokey, since I don't want the caller to have to explicitly specify
> the type for the range constants.
>
> Is there a way to declare the template such that the types of the 2nd and
> 3rd parameter are always deduced from the type of the first argument? I'm
> pretty sure I could give parameters 2 & 3 a different type declaration, and
> rely on runtime conversions between the different types, but I'd like this
> resolved at compile time; also, that approach might fail if vmin was a
> variable and vmax was a constant.
>
> Any suggestions?


You can use "concepts" to implement "mixed-mode" arithmetic functions.
The first thing you need is a C++03 emulation of concepts:

template <bool, class T = void> struct where {};
template <class T> struct where<true, T> {typedef T type;};

This is more famously known as "enable_if" and can be found at
www.boost.org. I've also called it "restrict_to" in the past. But it's
the same beast whatever you call it.

With this struct, and maybe a little help from std::tr1::type_traits (or
boost::type_traits) you can constrain your template parameters. In this
case you could make all three arguments different template parameters
(say T, U and V), but constrain U and V such that they are both
convertible to T. That would look like:

#include <tr1/type_traits>

template <bool, class T = void> struct where {};
template <class T> struct where<true, T> {typedef T type;};

template<typename T, class U, class V>
typename where
<
std::tr1::is_convertible<U, T>::value &&
std::tr1::is_convertible<V, T>::value,
T
>::type

ForceNumericRange( const T& val, const U& vmin, const V& vmax)
{
T retVal = val;

if ( retVal < vmin )
retVal = vmin;
else if ( retVal > vmax )
retVal = vmax;

return retVal;
}

Other constraints might work for you as well. For example you might
want to constrain each of T, U and V to be arithmetic types:

template<typename T, class U, class V>
typename where
<
std::tr1::is_arithmetic<T>::value &&
std::tr1::is_arithmetic<U>::value &&
std::tr1::is_arithmetic<V>::value,
T
>::type

ForceNumericRange( const T& val, const U& vmin, const V& vmax);

There is a big push on the standards committee to make concepts part of
the language for C++0X. This would clean up the syntax and make it
easier to build and reuse complex constraints. If accepted, it will
probably use "where" as a keyword. So if you use "where" now, note that
it may not compile in the future. You can view this as either a feature
or a bug. When it doesn't compile, you'll know it is time to upgrade to
the language supported variant.

Finally, you could go with 3 unconstrained templates:

template<typename T, class U, class V>
T
ForceNumericRange( const T& val, const U& vmin, const V& vmax);

This is what I refer to as "overly generic" code and is prone to
breakage in the case that the function name is part of an overload set
(if ForceNumericRange is overloaded, even in other namespaces). With a
name like ForceNumericRange, unintended overloading seems unlikely. But
it is easy to fall into the trap of using overly generic coding
techniques with very common names (as was done in the standard):

distance
advance
copy
fill
rotate

-Howard

Howard Hinnant 06-13-2006 02:28 PM

Re: Forcing all function template parameters to deduce the same type
 
In article
<howard.hinnant-C08735.10214213062006@syrcnyrdrs-02-ge0.nyroc.rr.com>,
Howard Hinnant <howard.hinnant@gmail.com> wrote:

> You can use "concepts" to implement "mixed-mode" arithmetic functions.
> The first thing you need is a C++03 emulation of concepts:
>
> template <bool, class T = void> struct where {};
> template <class T> struct where<true, T> {typedef T type;};


Oh, I got so carried away with concepts that I neglected to give you one
of the simplest techniques. :-)

You can do exactly this:

> Is there a way to declare the template such that the types of the 2nd and
> 3rd parameter are always deduced from the type of the first argument?


with the following code:

template <class T>
struct identity
{
typedef T type;
};

template<typename T>
T
ForceNumericRange(const T& val, const typename identity<T>::type& vmin,
const typename identity<T>::type& vmax);

I.e. this puts the second and third arguments into a non-deducible
context. And you still have the option of further constraining T using
"where" if necessary.

-Howard


All times are GMT. The time now is 08:08 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.