Thread: Specializing inner template based on static const member of outer template parameter

  1. #1
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396

    Specializing inner template based on static const member of outer template parameter

    Take for instance the following program:

    Code:
    #include <iostream>
    
    class A
    {
    public:
        static const int N = 6;
    };
    
    template <typename AParam>
    class B
    {
    public:
        template <int X>
        static void foo()
        {
            std::cout << "I'm X" << std::endl;
        }
    
        template <>
        static void foo<AParam::N>()
        {
            std::cout << "I'm the magic value" << std::endl;
        }
    };
    
    int main()
    {
        B<A>::foo<1>();
        B<A>::foo<6>();
    }
    With Visual C++ 2010, this compiles and outputs the following, which is what I would like:

    Code:
    I'm X
    I'm the magic value
    gcc however will not compile the code:

    Code:
    test.cpp:20:15: error: explicit specialization in non-namespace scope 'class B<AParam>'
    test.cpp:21:32: error: template-id 'foo<AParam:: N>' in declaration of primary template
    Why not? Which compiler is wrong?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Why not?
    You can't specialize a nested template without specializing its containing template.

    [Edit]
    Oh, but you can specialize the behavior through "CRTP" and indirection.

    Let me know if you need an example.
    [/Edit]

    Which compiler is wrong?
    MSVC2k10 is wrong.

    In the future, try checking Comeau out at their website.

    [Edit]
    Oh, and the use of the nested datum from the parameter isn't the problem.
    [/Edit]

    Soma
    Last edited by phantomotap; 04-06-2011 at 12:34 AM. Reason: none of your business

  3. #3
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by phantomotap View Post
    You can't specialize a nested template without specializing its containing template.
    Hmm. Well, I can take <int X> off of the function and put it on class B, and specialize class B. It would have been more compact the way I have written it, but oh well.

    What I'm really trying to do is refactor this:

    Code:
    int x = GetValue();
    int y = GetValue();
    if      (x == 0 && y == 0) Foo<0, 0>();
    else if (x == 0 && y == 1) Foo<0, 1>();
    else if (x == 0 && y == 2) Foo<0, 2>();
    else if (x == 1 && y == 0) Foo<1, 0>();
    else if (x == 1 && y == 1) Foo<1, 1>();
    else if (x == 1 && y == 2) Foo<1, 2>();
    else if (x == 2 && y == 0) Foo<2, 0>();
    else if (x == 2 && y == 1) Foo<2, 1>();
    else if (x == 2 && y == 2) Foo<2, 2>();
    In reality the two quantities take on 8 different values, that's 64 combinations, and I don't want to hand-write the above pairings. Maybe it's not obvious how my original question fits into this, but it has to do with how I'm representing the sequence of values the specializations go through, and handling the recursion in the metaprogram through the different combinations.

    If someone has a slick way of doing the above I'd love to see it.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Well, I can take <int X> off of the function and put it on class B, and specialize class B. It would have been more compact the way I have written it, but oh well.
    Solving the wrong problem; the "CRTP" indirection is a better solution to the problem in the original post. (That of specializing or partially specializing nested templates.)

    What I'm really trying to do is refactor this
    The original proposition can't help with that. You can't use values unknown at compile time as parameters to templates so the variables `x' and `y' couldn't be involved.

    If that pattern holds, you could write an ugly macro to inline most of that code by iteration. The overhead of writing and then maintaining that macro without the support of Boost is uglier than being explicit. Even then, I'd argue against it because it will only serve to hide the pattern behind a wall of ugly.

    If there is no pattern, and each case is important, you can still use a few macros, but the cases themselves must be noted somewhere. You would be gaining a few characters at the loss of clarity.

    Maybe it's not obvious how my original question fits into this, but it has to do with how I'm representing the sequence of values the specializations go through, and handling the recursion in the metaprogram through the different combinations.
    If you can post a better example illustrating your target recursion I can probably help you.

    If the variables are themselves actually known constants, themselves being parameters to templates, for example, I can help you derive a solution without being so explicit. Actually, the method I noted will happily help you with this, but there are lots of ways depending on the use case.

    If the variables really are variables, well, see above.

    Soma

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Here's what I want to do. I have two variables, call them X and Y. They only take on values from 0 through 7. Imagine for a moment we're in crazy land and I could write something like this:

    Code:
    int foo(int x, int y)
    {
        invoke<x, y>();
    }
    That expresses what I want to do, though of course I can't actually do that. But because X and Y are limited in range, I can achieve this by explicitly listing the possibilities:

    Code:
    int foo(int x, int y)
    {
        if (x == 0 && y == 0) invoke<0, 0>();
        if (x == 0 && y == 1) invoke<0, 1>();
        if (x == 0 && y == 2) invoke<0, 2>();
        // I don't have to go on forever, because only 0 through 7 will ever occur
    }
    Now, that's FINE, but I want to make this generic because this pattern comes up several times. In a previous version, I used a header file to do this:

    Code:
    // InvokePair.h
    INVOKE_PAIR(0,0)
    INVOKE_PAIR(0,1)
    INVOKE_PAIR(0,2)
    ...
    // Other code
    #define INVOKE_PAIR(X, Y) if(X == x && Y == y) Invoke<X, Y>();
    #include "InvokePair.h"
    #undef INVOKE_PAIR
    But I f'ing hate that. Now I have some other script which generates InvokePair.h for me. Thus I want to solve the problem once and for all with a template recursion.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Thus I want to solve the problem once and for all with a template recursion.
    Ah, so you don't actually even want what I thought.

    So, before I give this any more thought, let me verify my current understanding.

    1) The variables in question are truly variables, expressions involving values not known at compile time.

    2) You don't mind if the tests are performed at runtime so long as the logic isn't explicitly stated for every case.

    3) You don't necessarily always invoke the same function, but the relationship between the values and the target function is expressed by explicit template function invocation.

    4) The values to be correlated follow a set pattern.

    5) The values always have a known and limited range.

    6) The values express some association unto themselves. (They are a pair or tuple of some kind.)

    [Edit]
    7) You realize that template recursion and iteration always involves recursion.

    8) As the goal requires runtime evaluation of an expression the solution involving template recursion will necessarily involve calling a recursive function template at runtime for each value or combination of values in the target correlation every time the interface function is called.
    [/Edit]

    Soma
    Last edited by phantomotap; 04-06-2011 at 12:29 PM. Reason: none of your business

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    All six of your assumptions are correct.

    Code:
    template <template <int, int> invoke>
    int foo(int x, int y)
    {
        invoke<x, y>();
    }
    Again, I realize that's not C++ but it expresses exactly what I want. The template parameters to invoke<> are always numerically equal to the runtime variables x and y. I suppose for generality they could be functions of x and y, but that's easy to add later and unnecessary for the moment.

    I also realize comparisons will be made at runtime, that's perfectly okay, I just don't want to have to spell it all out manually.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    All six of your assumptions are correct.
    Easy.

    I've included the simplest two value core with explicit specialization for the range you've noted as necessary.

    But, I have left all higher level facilities to you. It would be easy to add code to the core to allow arbitrary range, functions to do the adjustment between iterations, and a generalized wrapper. I believe that any of that would only contrive to make the example more difficult to understand.




    I realize that's not C++ but it expresses exactly what I want.
    *shrug*

    I know you aren't an idiot. I just didn't want to waste my time with something impossible.

    Most of my best freelance jobs are fixing meta-programming gone wrong.

    On the other hand, all of the worst jobs come from people wanting the impossible.

    I'm sure you understand where I'm coming from.

    Soma

    Code:
    template
    <
        int TValue1
      , int TValue2
      , typename TWrappedFunction
    >
    struct Invoker
    {
        inline static bool Go
        (
            int fValue1
          , int fValue2
        )
        {
            if((TValue1 == fValue1) && (TValue2 == fValue2))
            {
                TWrappedFunction::template Apply<TValue1, TValue2>::Go();
                return(true);
            }
            else
            {
                return(false);
            }
        }
    };
    
    template
    <
        int TValue1
      , int TValue2
      , typename TWrappedFunction
      , int TOriginalValue1 = TValue1
      , int TOriginalValue2 = TValue2
    >
    struct InterfaceBuilder // General Condition
    {
        inline static void Go
        (
            int fValue1
          , int fValue2
        )
        {
            if(!Invoker<TValue1, TValue2, TWrappedFunction>::Go(fValue1, fValue2))
            {
                InterfaceBuilder<TValue1, TValue2 - 1, TWrappedFunction, TOriginalValue1, TOriginalValue2>::Go(fValue1, fValue2);
            }
        }
    };
    
    template
    <
        int TValue1
      , typename TWrappedFunction
      , int TOriginalValue1
      , int TOriginalValue2
    >
    struct InterfaceBuilder<TValue1, 0, TWrappedFunction, TOriginalValue1, TOriginalValue2> // Cyclical Condition
    {
        inline static void Go
        (
            int fValue1
          , int fValue2
        )
        {
            if(!Invoker<TValue1, 0, TWrappedFunction>::Go(fValue1, fValue2))
            {
                InterfaceBuilder<TValue1 - 1, TOriginalValue2, TWrappedFunction, TOriginalValue1, TOriginalValue2>::Go(fValue1, fValue2);
            }
        }
    };
    
    template
    <
        typename TWrappedFunction
      , int TOriginalValue1
      , int TOriginalValue2
    >
    struct InterfaceBuilder<0, 0, TWrappedFunction, TOriginalValue1, TOriginalValue2> // Terminating Condition
    {
        inline static void Go
        (
            int fValue1
          , int fValue2
        )
        {
            Invoker<0, 0, TWrappedFunction>::Go(fValue1, fValue2);
        }
    };
    
    
    
    
    #include <iostream>
    
    template
    <
        int TValue1
      , int TValue2
    >
    void TergetFunction()
    {
        std::cout << "a general function: " << TValue1 << ':' << TValue2 << '\n';
    }
    
    template <> void TergetFunction<2, 3>()
    {
        std::cout << "a special function: " << 2 << ':' << 3 << '\n';
    }
    
    template <> void TergetFunction<7, 4>()
    {
        std::cout << "a different special function: " << 7 << ':' << 4 << '\n';
    }
    
    struct TergetFunctionWrapper
    {
        template
        <
            int TValue1
          , int TValue2
        >
        struct Apply
        {
            inline static void Go()
            {
                TergetFunction<TValue1, TValue2>();
            }
        };
    };
    
    void TergetFunctionInterface
    (
        int fValue1
      , int fValue2
    )
    {
        InterfaceBuilder<8, 8, TergetFunctionWrapper>::Go(fValue1, fValue2);
    }
    
    
    
    
    int main()
    {
        int lValue1(0);
        int lValue2(0);
        std::cout << "enter two target values: _\b";
        std::cin >> lValue1 >> lValue2;
        TergetFunctionInterface(lValue1, lValue2);
        return(0);
    }

  9. #9
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I got it working with the following:

    EDIT: Your solution allows some additional flexibility that I may need later, so thanks for posting it.

    Code:
    #include <iostream>
    
    template <int X, int Y>
    class MyInvoke
    {
    public:
    	static void Invoke()
    	{
    		std::cout << X << ", " << Y << std::endl;
    	}
    };
    
    template <int X, int Y, template <int, int> class F>
    class PairInvokerHelper
    {
    public:
    	static void Invoke(int x, int y)
    	{
    		PairInvoker<X, Y+1, F>::Invoke(x, y);
    	}
    };
    
    template <int X, template <int, int> class F>
    class PairInvokerHelper<X, 7, F>
    {
    public:
    	static void Invoke(int x, int y)
    	{
    		PairInvoker<X+1, 0, F>::Invoke(x, y);
    	}
    };
    
    template <template <int, int> class F>
    class PairInvokerHelper<7, 7, F>
    {
    public:
    	static void Invoke(int x, int y)
    	{
    	}
    };
    
    
    template <int X, int Y, template <int, int> class F>
    class PairInvoker
    {
    public:
    	static void Invoke(int x, int y)
    	{
    		if (X == x && Y == y)
    			F<X, Y>::Invoke();
    		else
    			PairInvokerHelper<X, Y, F>::Invoke(x, y);
    	}
    };
    
    template <template <int, int> class F>
    void PairInvoke(int x, int y)
    {
    	PairInvoker<0, 0, F>::Invoke(x, y);
    }
    
    int main()
    {
    	for (int x = 0; x < 8; ++x)
    	{
    		for (int y = 0; y < 8; ++y)
    		{
    			PairInvoke<MyInvoke>(x, y);
    		}
    	}
    }
    Last edited by brewbuck; 04-06-2011 at 01:38 PM.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  10. #10
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    LMAO!

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Reference Counting
    By Dae in forum C++ Programming
    Replies: 10
    Last Post: 08-13-2009, 07:34 AM
  2. LNK2001 ERROR!!! need help
    By lifeafterdeath in forum C++ Programming
    Replies: 7
    Last Post: 05-27-2008, 05:05 PM
  3. C bubble sort?
    By fredanthony in forum C Programming
    Replies: 11
    Last Post: 02-13-2006, 09:54 PM
  4. Replies: 6
    Last Post: 12-06-2005, 09:23 AM
  5. oh me oh my hash maps up the wazoo
    By DarkDays in forum C++ Programming
    Replies: 5
    Last Post: 11-30-2001, 12:54 PM