Boost logo

Boost :

From: Daryle Walker (dwalker07_at_[hidden])
Date: 2003-05-14 17:52:38


On Monday, May 12, 2003, at 10:59 AM, Hubert Holin wrote:

> I believe, and hope, I got something wrong.

Didn't I warn you about taking the approach you describe below? I
think it was over a year ago (but I changed computers so I can't check
at the moment).

> Updating the special functions library (alongside coding for
> prospective new members thereof), I hit a problem, which I can't solve
> in a satisfactory manner. It boils down to the following situation:
>
> <CODE>
>
> #include <complex>
> #include <iostream>
> #include <valarray>
>
>
> // (1) Plain vanilla templated function
>
> template<typename T>
> T f(T x)
> {
> ::std::cout << "Plain vanilla templated function" << ::std::endl;
>
> return(x);
> }
>
>
> // (2) Template-template function
>
> template<typename T, template<typename> class U>
> U<T> f(U<T> x)
> {
> ::std::cout << "Template-template function" << ::std::endl;
>
> return(x);
> }
>
>
> // (3) Valarray function
>
> template<typename T>
> ::std::valarray<T> f(const ::std::valarray<T> & x)
> {
> ::std::cout << "Valarray function" << ::std::endl;
>
> ::std::valarray<T> result = x;
>
> return(result);
> }
>
>
>
> int main()
> {
> double x(1);
>
> f(x);
>
> ::std::complex<double> y(0,1);
>
> f(y);
>
> const ::std::valarray<double> z(3);
>
> f(z);
> }
>
> </CODE>
>
> Let us say that f is sinc. In the current library, I have forms
> (1), for the usual floating point types, and (2), for complex,
> quaternion and octonions. I wanted to add (3) for valarrays of
> floating point types (and later, yet another form for valarrays of
> complex et al), along what exists for, say, sin.
>
> This turns out to be impossible, at least according to my
> compiler, which complains about an ambiguity in overload resolution
> between (2) and (3) (if I comment out (2) or (3) it all compiles, with
> naturally wrong behaviour...). Explicit instantiation (those I have
> tried, at least...) does not help.
>
> I understand that (1) and (2) are unordered (neither is more
> specialized than the other). It is also clear that (3) is more
> specialized than (1). It is less clear to me that (3) should also be
> more specialized than (2).

Actually, I think that (3) should be more specialized than (2). Maybe
your compiler is messed up or the standard needs another DR.

> One possible, partial, remedie I have in mind would be to remove
> (2), add corresponding specializations in the headers for quaternions
> and octonions, and add specialization for complex and valarray in
> sinc's header (can't modify <complex> or <valarray> as they are
> standard). But this is most inelegant, as the code for three
> specializations is exactly the same, except for types, and this is
> precisely the reason for using (2)! Furthermore, if we want to add new
> functions that may work on complex, quaternions and octonions, we
> would have to modify their headers for each and every one. That's an
> appaling lack of orthogonality! Another approach would be to leave the
> headers for quaternions and octonions alone, and stuff sinc's header
> with lots of specilizations, most of which really are instanciations
> of (2), and that's both inelegant and a possible maintenance problem.
>
> I welcome any suggestion on that matter (and would most welcome
> somebody showing me what I got wrong :-) ).

Here's my objections from before:

1. Let's call the complex, quaternion, and octonion types the Cayley
family.

2. You specialize certain functions with the signature:

        template < typename T, template <typename> class U >
                f( U<T> x );

        with the algorithms that the Cayley-family types need.

3. But you _assumed_ that the set of C++'ed Cayley types and
        the set of types using the U<T> signature are identical.

4. I say that you have a FUNDAMENTALLY WRONG assumption! You
        can have:
        a. Cayley types with a different template signature, or be
                non-template types
        b. Non-Cayley types with the given template signature

When I gave the warning, I was being theoretical, and you ignored it.
But you finally found a real-life example of the warning (part 4b at
least).

I see that Daniel Frey gave a workaround. Don't use it; it's a
convoluted solution for something that started fundamentally wrong.

Since partially specializing function templates works strange, you may
want to use the "function template calling a class template" idiom.

//===============================================================
namespace detail
{
        template < typename T >
        T my_sinc_real( T x );

        template < typename T >
        T my_sinc_nonscalar_cayley( T x );

        template < typename T >
        T my_sinc_vector( T x );
}

template < typename T >
class sinc_functor
{
        T operator ()( T x );
};

// Add appropriate specializations

template < typename T >
T sinc ( T x )
{
        return sinc_functor<T>()( x );
}
//===============================================================

Of course, your main problem is how to specialize the C++ Cayley types
we have without repeating a lot of code, but making sure to miss
inappropriate types. Maybe we can use some sort of traits system to
determine specializations? We'll have to think about this some more....

Daryle


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk