How to work around 14.5.5, 14.7.3/18 and related?

Discussion in 'C++' started by Stephen Waits, Apr 7, 2004.

  1. Occasionally we want to perform a Sqrt where we don't need accuracy
    and/or we have a good initial guess - a situation where an N-step Sqrt
    series may be more optimal than a full on Sqrt(). For example:

    template <unsigned int iterations>
    T SqrtApprox(const T x, const T guess)
    {
    return SqrtApprox<iterations-1>( x, T(0.5f) * (guess + (x/guess)) );
    }

    template <>
    T SqrtApprox<0>(const T x, const T guess)
    {
    return guess;
    }

    The problems came when I attempted to get this little template
    metaprogram into our main Math<T> class which looks something like
    this:

    template <typename T>
    class Math
    {
    public:
    static T Sqrt(T x);
    static T Sin(T x);
    static T ASin(T x);
    ... // SqrtApprox should be in here
    };


    So, I've coded up a few simple examples describing the process I've
    gone through in trying to get this to work...


    This working example shows basically what I want to do - only in a
    global scope.

    template <unsigned int I>
    float Iterative(float x)
    {
    return x + Iterative<I-1>(x);
    }

    template <>
    float Iterative<0>(float x)
    {
    // specialize 0th iteration to stop template recursion
    return 0.0f;
    }

    However, when I decide to put this into a class and template out
    "float" (similar to the Math<T> class above) I run into problems with
    the language; for example:

    [Begin BROKEN example

    template <class T>
    class SomeClass
    {
    public:

    template <unsigned int I>
    static T Iterative(T x);
    };

    template <class T>
    template <unsigned int I> // not allowed!
    T SomeClass<T>::Iterative<I>(T x)
    {
    }

    --end BROKEN example]

    Because this is a static member I cannot specialize it (because I
    believe this is a function template, of which specialization is NOT
    allowed by C++). Correct me if I'm wrong.

    So, my next step is attempting to put this in a nested functor:

    [Begin BROKEN example

    template <class T>
    class SomeClass
    {
    public:
    template <unsigned int I>
    class Iterative
    {
    public:
    static T IterativeImpl(T x);
    };
    };


    template <class T>
    template <unsigned int I>
    T SomeClass<T>::Iterative<I>::IterativeImpl(T x)
    {
    }

    template <class T>
    template <> // not allowed!
    T SomeClass<T>::Iterative<0>::IterativeImpl(T x)
    {
    }

    --end BROKEN example]

    And, of course, as you experts know already, specialization of this
    nested class is not allowed because the enclosing class template is
    not specialized. Again, correct me if I'm wrong here.

    What do you suggest I do to work around these issues?

    Thanks,
    Steve

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Stephen Waits, Apr 7, 2004
    #1
    1. Advertising

  2. Stephen Waits

    bartek Guest

    (Stephen Waits) wrote in
    news::

    > Occasionally we want to perform a Sqrt where we don't need accuracy
    > and/or we have a good initial guess - a situation where an N-step Sqrt
    > series may be more optimal than a full on Sqrt(). For example:


    (...)

    > So, my next step is attempting to put this in a nested functor:
    >
    > [Begin BROKEN example
    >
    > template <class T>
    > class SomeClass
    > {
    > public:
    > template <unsigned int I>
    > class Iterative
    > {
    > public:
    > static T IterativeImpl(T x);
    > };
    > };
    >
    >
    > template <class T>
    > template <unsigned int I>
    > T SomeClass<T>::Iterative<I>::IterativeImpl(T x)
    > {
    > }
    >
    > template <class T>
    > template <> // not allowed!
    > T SomeClass<T>::Iterative<0>::IterativeImpl(T x)
    > {
    > }
    >
    > --end BROKEN example]
    >
    > And, of course, as you experts know already, specialization of this
    > nested class is not allowed because the enclosing class template is
    > not specialized. Again, correct me if I'm wrong here.
    >
    > What do you suggest I do to work around these issues?


    Actually I've been using this technique quite often. The difference was
    though, that I've been putting the nested class definition inside the
    outer class definition body.

    The following works for me:

    template <typename T>
    struct Outer {
    template <unsigned i>
    struct Inner {
    static T pow(T x)
    { return Inner<i-1>::pow(x)*x; }
    };
    template <>
    struct Inner<1> {
    static T pow(T x)
    { return x; }
    };
    template <>
    struct Inner<0> {
    static T pow(T x)
    { return 1; }
    };
    };

    #include <iostream>

    int main()
    {
    std::cout << Outer<float>::Inner<4>::pow(2) << std::endl;
    return 0;
    }

    PS: Don't cross-post to a moderated group at the same time you're posting
    here. You'll get more replies quicker. :)
     
    bartek, Apr 7, 2004
    #2
    1. Advertising

  3. Stephen Waits

    tom_usenet Guest

    On Wed, 7 Apr 2004 15:05:46 +0000 (UTC), bartek
    <2.pl> wrote:

    >Actually I've been using this technique quite often. The difference was
    >though, that I've been putting the nested class definition inside the
    >outer class definition body.


    But adding specializations there is illegal - specializations may only
    appear at namespace scope.

    >The following works for me:
    >
    >template <typename T>
    >struct Outer {
    > template <unsigned i>
    > struct Inner {
    > static T pow(T x)
    > { return Inner<i-1>::pow(x)*x; }
    > };
    > template <>
    > struct Inner<1> {
    > static T pow(T x)
    > { return x; }
    > };
    > template <>
    > struct Inner<0> {
    > static T pow(T x)
    > { return 1; }
    > };
    >};
    >
    >#include <iostream>
    >
    >int main()
    >{
    > std::cout << Outer<float>::Inner<4>::pow(2) << std::endl;
    > return 0;
    >


    (Comeau C++ strict mode)

    "main.cpp", line 9: error: explicit specialization is not allowed in
    the current scope
    template <>
    ^

    Comeau compiles it in "Microsoft mode", and VC7.1 compiles it, so I
    guess it is a Microsoft extension.

    Tom
    --
    C++ FAQ: http://www.parashift.com/c -faq-lite/
    C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
     
    tom_usenet, Apr 7, 2004
    #3
  4. Stephen Waits

    tom_usenet Guest

    On 7 Apr 2004 10:26:04 -0400, (Stephen Waits) wrote:

    >Occasionally we want to perform a Sqrt where we don't need accuracy
    >and/or we have a good initial guess - a situation where an N-step Sqrt
    >series may be more optimal than a full on Sqrt(). For example:
    >
    >template <unsigned int iterations>
    >T SqrtApprox(const T x, const T guess)
    >{
    > return SqrtApprox<iterations-1>( x, T(0.5f) * (guess + (x/guess)) );
    >}
    >
    >template <>
    >T SqrtApprox<0>(const T x, const T guess)
    >{
    > return guess;
    >}
    >
    >The problems came when I attempted to get this little template
    >metaprogram into our main Math<T> class which looks something like
    >this:
    >
    >template <typename T>
    >class Math
    >{
    >public:
    > static T Sqrt(T x);
    > static T Sin(T x);
    > static T ASin(T x);
    > ... // SqrtApprox should be in here
    >};
    >
    >
    >So, I've coded up a few simple examples describing the process I've
    >gone through in trying to get this to work...


    >Because this is a static member I cannot specialize it (because I
    >believe this is a function template, of which specialization is NOT
    >allowed by C++). Correct me if I'm wrong.


    IIRC you can specialize a member function template, as long as the
    enclosing class is completely specialized.

    >And, of course, as you experts know already, specialization of this
    >nested class is not allowed because the enclosing class template is
    >not specialized. Again, correct me if I'm wrong here.


    No, you're not wrong.

    >What do you suggest I do to work around these issues?


    Overloading? Or you could rely on the optimizer to do a good job
    passing the iteration as a normal parameter. Anyway, here's an
    overloading based approach, which works around the problem without
    needing to change the call syntax:

    template <unsigned int N>
    struct UnsignedInt
    {
    static unsigned int const value = N;
    };

    template <typename T>
    class Math
    {
    public:
    template <unsigned int iterations>
    static T SqrtApprox(const T x, const T guess,
    UnsignedInt<iterations>* = 0)
    {
    return SqrtApprox(
    x,
    T(0.5f) * (guess + (x/guess)),
    (UnsignedInt<iterations - 1>*)0
    );
    }

    private:
    static T SqrtApprox(const T x, const T guess, UnsignedInt<0>*)
    {
    return guess;
    }
    };

    #include <iostream>

    int main()
    {
    std::cout << Math<double>::SqrtApprox<10>(9.5, 3.1) << '\n';
    }

    There may be other workarounds too.

    Tom
    --
    C++ FAQ: http://www.parashift.com/c -faq-lite/
    C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
     
    tom_usenet, Apr 7, 2004
    #4
  5. Stephen Waits

    bartek Guest

    tom_usenet <> wrote in
    news::

    > But adding specializations there is illegal - specializations may only
    > appear at namespace scope.
    >
    >>The following works for me:
    >>
    >>template <typename T>
    >>struct Outer {
    >> template <unsigned i>
    >> struct Inner {
    >> static T pow(T x)
    >> { return Inner<i-1>::pow(x)*x; }
    >> };
    >> template <>
    >> struct Inner<1> {
    >> static T pow(T x)
    >> { return x; }
    >> };
    >> template <>
    >> struct Inner<0> {
    >> static T pow(T x)
    >> { return 1; }
    >> };
    >>};
    >>
    >>#include <iostream>
    >>
    >>int main()
    >>{
    >> std::cout << Outer<float>::Inner<4>::pow(2) << std::endl;
    >> return 0;
    >>

    >
    > (Comeau C++ strict mode)
    >
    > "main.cpp", line 9: error: explicit specialization is not allowed in
    > the current scope
    > template <>
    > ^
    >
    > Comeau compiles it in "Microsoft mode", and VC7.1 compiles it, so I
    > guess it is a Microsoft extension.


    OOPS...

    Funny though - I'm using VC6. I don't quite get the point of such
    extensions, considering how limited is VC6's support for templates,
    except for opening ways for portability issues. sheesh.

    Thanks for clearing this up.

    Cheers!
     
    bartek, Apr 7, 2004
    #5
  6. Stephen Waits

    bartek Guest

    tom_usenet <> wrote in
    news::

    > On 7 Apr 2004 10:26:04 -0400, (Stephen Waits) wrote:
    >
    >>Occasionally we want to perform a Sqrt where we don't need accuracy
    >>and/or we have a good initial guess - a situation where an N-step Sqrt
    >>series may be more optimal than a full on Sqrt(). For example:
    >>
    >>template <unsigned int iterations>
    >>T SqrtApprox(const T x, const T guess)
    >>{
    >> return SqrtApprox<iterations-1>( x, T(0.5f) * (guess + (x/guess)) );
    >>}
    >>
    >>template <>
    >>T SqrtApprox<0>(const T x, const T guess)
    >>{
    >> return guess;
    >>}
    >>
    >>The problems came when I attempted to get this little template
    >>metaprogram into our main Math<T> class which looks something like
    >>this:
    >>
    >>template <typename T>
    >>class Math
    >>{
    >>public:
    >> static T Sqrt(T x);
    >> static T Sin(T x);
    >> static T ASin(T x);
    >> ... // SqrtApprox should be in here
    >>};
    >>
    >>
    >>So, I've coded up a few simple examples describing the process I've
    >>gone through in trying to get this to work...

    >
    >>Because this is a static member I cannot specialize it (because I
    >>believe this is a function template, of which specialization is NOT
    >>allowed by C++). Correct me if I'm wrong.

    >
    > IIRC you can specialize a member function template, as long as the
    > enclosing class is completely specialized.
    >
    >>And, of course, as you experts know already, specialization of this
    >>nested class is not allowed because the enclosing class template is
    >>not specialized. Again, correct me if I'm wrong here.

    >
    > No, you're not wrong.
    >
    >>What do you suggest I do to work around these issues?

    >
    > Overloading? Or you could rely on the optimizer to do a good job
    > passing the iteration as a normal parameter. Anyway, here's an
    > overloading based approach, which works around the problem without
    > needing to change the call syntax:
    >
    > template <unsigned int N>
    > struct UnsignedInt
    > {
    > static unsigned int const value = N;
    > };
    >
    > template <typename T>
    > class Math
    > {
    > public:
    > template <unsigned int iterations>
    > static T SqrtApprox(const T x, const T guess,
    > UnsignedInt<iterations>* = 0)
    > {
    > return SqrtApprox(
    > x,
    > T(0.5f) * (guess + (x/guess)),
    > (UnsignedInt<iterations - 1>*)0
    > );
    > }
    >
    > private:
    > static T SqrtApprox(const T x, const T guess, UnsignedInt<0>*)
    > {
    > return guess;
    > }
    > };
    >
    > #include <iostream>
    >
    > int main()
    > {
    > std::cout << Math<double>::SqrtApprox<10>(9.5, 3.1) << '\n';
    > }
    >
    > There may be other workarounds too.


    Indeed :) the following uses a separate template class to delegate work
    into. Funny ... my compiler gives me an internal error with this. :)

    template <unsigned n>
    struct SqrtApproxImpl {
    template <typename T>
    static T eval(const T x, const T guess)
    {return SqrtApproxImpl<n-1>::eval(x, T(0.5f) * (guess + (x/guess)));}
    };

    template <>
    struct SqrtApproxImpl<0> {
    template <typename T>
    static T eval(const T x, const T guess)
    {return guess;}
    };

    template <typename T>
    struct Math {
    template <unsigned n>
    static T SqrtApprox(const T x, const T guess)
    { return SqrtApproxImpl<n>::eval(x, guess); }
    };

    #include <iostream>

    int main()
    {
    std::cout << Math<double>::SqrtApprox<10>(9.5, 3.1) << std::endl;
    return 0;
    }
     
    bartek, Apr 7, 2004
    #6
  7. Stephen Waits wrote in news:f327e120.0404061034.73bffbf2
    @posting.google.com:

    > And, of course, as you experts know already, specialization of this
    > nested class is not allowed because the enclosing class template is
    > not specialized. Again, correct me if I'm wrong here.
    >
    > What do you suggest I do to work around these issues?
    >


    #include <iostream>
    #include <ostream>

    template <unsigned int I>
    struct iterative_helper
    {
    template < typename Float >
    static Float apply( Float x )
    {
    return x + iterative_helper<I-1>::apply( x );
    }
    };

    template <>
    struct iterative_helper< 0U >
    {
    template < typename Float >
    static Float apply( Float x )
    {
    return Float();
    }
    };

    template < typename T >
    class SomeClass
    {
    public:

    template <unsigned int I>
    static T iterative( T x );
    };

    template < typename T >
    template <unsigned int I>
    T SomeClass< T >::iterative( T x )
    {
    return iterative_helper< I >::apply( x );
    }

    int main()
    {
    std::cout << SomeClass< float >::iterative< 32 >( 22 ) << std::endl;
    }

    Rob.
    --
    http://www.victim-prime.dsl.pipex.com/

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Rob Williscroft, Apr 7, 2004
    #7
  8. bartek wrote:

    [in-class template member specialisation]

    > Funny though - I'm using VC6. I don't quite get the point of
    > such extensions, considering how limited is VC6's support for
    > templates, except for opening ways for portability issues.


    I believe that VC6 allows in-class specialisation because it doesn't
    handle out-of-class template member definitions. If that's true, VC7
    probably is to be backward-compatible.


    Martin
     
    Martin Eisenberg, Apr 7, 2004
    #8
  9. Thanks to all. Helper idea looks like it should work great..

    --Steve
     
    Stephen Waits, Apr 8, 2004
    #9
  10. Stephen Waits wrote:

    []

    > This working example shows basically what I want to do - only in a
    > global scope.
    >
    > template <unsigned int I>
    > float Iterative(float x)
    > {
    > return x + Iterative<I-1>(x);
    > }
    >
    > template <>
    > float Iterative<0>(float x)
    > {
    > // specialize 0th iteration to stop template recursion
    > return 0.0f;
    > }
    >
    > However, when I decide to put this into a class and template out
    > "float" (similar to the Math<T> class above) I run into problems with
    > the language; for example:
    >
    > [Begin BROKEN example
    >
    > template <class T>
    > class SomeClass
    > {
    > public:
    >
    > template <unsigned int I>
    > static T Iterative(T x);
    > };
    >
    > template <class T>
    > template <unsigned int I> // not allowed!
    > T SomeClass<T>::Iterative<I>(T x)
    > {
    > }
    >
    > --end BROKEN example]
    >
    > Because this is a static member I cannot specialize it (because I
    > believe this is a function template, of which specialization is NOT
    > allowed by C++). Correct me if I'm wrong.
    >


    To fix it throw out <I>

    template <class T>
    template <unsigned int I>
    T SomeClass<T>::Iterative(T x)
    {
    }

    > So, my next step is attempting to put this in a nested functor:
    >
    > [Begin BROKEN example
    >
    > template <class T>
    > class SomeClass
    > {
    > public:
    > template <unsigned int I>
    > class Iterative
    > {
    > public:
    > static T IterativeImpl(T x);
    > };
    > };
    >
    >
    > template <class T>
    > template <unsigned int I>
    > T SomeClass<T>::Iterative<I>::IterativeImpl(T x)
    > {
    > }
    >
    > template <class T>
    > template <> // not allowed!
    > T SomeClass<T>::Iterative<0>::IterativeImpl(T x)
    > {
    > }
    >
    > --end BROKEN example]
    >
    > And, of course, as you experts know already, specialization of this
    > nested class is not allowed because the enclosing class template is
    > not specialized. Again, correct me if I'm wrong here.
    >
    > What do you suggest I do to work around these issues?


    Use overloading instead of specialization:

    template<unsigned n>
    struct unsigned_ // resembles boost::mpl::int_<>
    {
    enum { value = n };
    };

    template<class T>
    struct some
    {
    template<unsigned n>
    static void do_some();

    template<unsigned n>
    static void do_some_imp(unsigned_<n>);

    static void do_some_imp(unsigned_<0>);
    };

    template<class T>
    template<unsigned n>
    void some<T>::do_some() { return do_some_imp(unsigned_<n>()); }

    template<class T>
    template<unsigned n>
    void some<T>::do_some_imp(unsigned_<n>) { return do_some_imp(unsigned_<n - 1>()); }

    template<class T>
    void some<T>::do_some_imp(unsigned_<0>) { ; }

    int main()
    {
    some<int>::do_some<32>();
    }

    --
    Maxim Yegorushkin
    MetaCommunications Engineering
    http://www.meta-comm.com/engineering/

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]
     
    Maxim Yegorushkin, Apr 8, 2004
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. TB
    Replies:
    2
    Views:
    3,741
  2. Piet
    Replies:
    0
    Views:
    547
  3. zirpu xunre pelxu

    bdist_rpm problems, and my work around.

    zirpu xunre pelxu, Aug 5, 2004, in forum: Python
    Replies:
    1
    Views:
    326
    Anthony Baxter
    Aug 5, 2004
  4. Maxwell Hammer
    Replies:
    7
    Views:
    644
    Peter Hansen
    Jun 18, 2005
  5. Network/Software Buyer
    Replies:
    0
    Views:
    420
    Network/Software Buyer
    May 23, 2010
Loading...

Share This Page