Lezkus
Lezkus

Reputation: 170

Template Mutual Recursion

I would like to have in my library a template function func such that the user can overload it with its own types. The problem is that my type system is of the form

T := A<T>, B<T>, C

As such, template<class T> void func(A<T>); needs template<class T> void func(B<T>); if it is specialised with T = B<C>. Reciprocally, if we instantiate func(B<T>) with T = A<C>, the B<T> specialisation needs the A<T> specialisation.

The problem until here could be solved declaring the template functions in some common header.

What I don't know how to approach is how to make this type system extensible. I would like that the user can define her own type template<class T> class D<T>; and implement her own template<class T> void func(D<T>);. In this case, I don't know how the user could forward declare her type so that in the specialisation A<D<C>> the function void func(A<T>); is able to find void func(D<T>);.

Is there any standard way of doing this?

Edit minimal working example of the problem:

// in A.hpp

namespace ns {
template<class T> struct A { T t; };
template<class T>
void fun (A<T> a) { fun(a.t); }
}

// B.hpp

namespace ns {
template<class T> struct B { T t; };
template<class T>
void fun (B<T> b) { fun(b.t); }

// C.hpp

#include <iostream>
namespace other {
template<class T>
struct C {};
}

namespace ns {
template<class T>
void fun(other::C<T> c) { std::cout << "C" << std::endl; }
}

// main.cpp

#include "A.hpp"
#include "B.hpp"
#include "C.hpp"

namespace ns {
void f () {
    fun(A<B<other::C<int>>>());
}
}


int main () {
    ns::f();
}

This example doesn't compile. It just compiles if we reorder the includes in main.cpp as

#include "C.hpp"
#include "B.hpp"
#include "A.hpp"

Now, this is clearly a hack. With this design the user will not be able to instantiate both A<B<C<int>>> and B<A<C<int>>>. A solution to this would be to forward declare A and B in some other template and include it in both A.hpp and B.hpp. The problem now comes when you try to let the user of the library define her own types. If the user of the library defines her own type template<class T> class D;, she cannot forward declare, and then, if she tries to instantiate A<D<C<int>>>, the compilation will fail.

In this example the namespace other represents a namespace over which I don't have control, and C represents a preexisting class in some other library. This can be thought as some boost class or similar. The ns namespace is the one that my library defines.

Upvotes: 0

Views: 464

Answers (1)

max66
max66

Reputation: 66230

What about if fun() is a static method in a template class?

So you can partial specialize the class?

I mean something like

// 000.h

#ifndef h_000__h
#define h_000__h

namespace ns
 {
   template <typename T>
   struct foo;
 }

#endif

// 001.h

#ifndef h_001__h
#define h_001__h

#include <iostream>

#include "000.h"

namespace ns
 {
   template<class T>
   struct A
    { T t; };

   template <typename T>
   struct foo<A<T>>
    {
      static void fun (A<T> a)
       { std::cout << "A<T> fun" << std::endl; foo<T>::fun(a.t); }
    };
 }

#endif

// 002.h

#ifndef h_002__h
#define h_002__h

#include <iostream>

#include "000.h"

namespace ns
 {
   template <typename T>
   struct B
    { T t; };

   template <typename T>
   struct foo<B<T>>
    {
      static void fun (B<T> a)
       { std::cout << "B<T> fun" << std::endl; foo<T>::fun(a.t); }
    };
 }

#endif

// 003.h

#ifndef h_003__h
#define h_003__h

#include <iostream>

#include "000.h"

namespace other
 {
   template <typename T>
   struct C
    { };
 }

namespace ns
 {
   template <typename T>
   struct foo<other::C<T>>
    {
      static void fun (other::C<T> a)
       { std::cout << "C<T> fun" << std::endl; }
    };
 }

#endif

// main.cpp

#include "001.h"
#include "002.h"
#include "003.h"

namespace ns
 {
   void f ()
    {
      using type = A<B<other::C<int>>>;

      foo<type>::fun(type{});
    }
 }

int main ()
 {
   ns::f(); // print A<T> fun \n B<T> fun \n C<T> fun \n
 }

Upvotes: 1

Related Questions