Reputation: 170
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
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