I have the following two struct declarations:
template <typename T>
struct Yin {
T *m_ptr;
};
template <typename T>
struct Yang {
T *m_ptr;
};
and I'd like to find X
and Y
such that I get something like this after substitution:
// Not real C++ syntax
struct Yin<X> {
Yang<Y> *m_ptr;
}
struct Yang<Y> {
Yin<X> *m_ptr;
};
But I'd like to do so without hard-coding Yin
and Yang
into one another's definitions, so X
would be something like Yin<Yang<Yin<Yang<...>>>>
.
I can do this without template arguments like:
struct Yin;
struct Yang;
struct Yin {
Yang *m_ptr;
};
struct Yang {
Yin *m_ptr;
};
But my real use-case is considerably more complex and I'd really like to make it generic. Does anyone know of a way to accomplish this? Or maybe see something obvious that I'm missing?
I've tagged this question as c++14
because I'm compiling the relevant code with clang with -std=c++1y
and I am happy to use any c++11/c++14 features to make this work.
Here is a solution that looks like it should work, but doesn't compile (and gives me useless error messages):
template <typename T>
struct Yin {
T *m_ptr;
};
template <typename T>
struct Yang {
T *m_ptr;
};
template <template <class> class A, template <class> class B>
struct knot {
using type = A<typename knot<B, A>::type>;
};
template <template <class> class A, template <class> class B>
using Tie = typename knot<A, B>::type;
int main() {
// foo.cc:13:39: error: no type named 'type' in 'knot<Yin, Yang>'
// using type = A<typename knot<B, A>::type>;
// ~~~~~~~~~~~~~~~~~~~~~^~~~
Tie<Yin, Yang> x;
}
Specialize Yin and Yang for when T
is a template type and void
is the template parameter, which causes Yin<Yang<void>>
to point at a Yang<Yin<void>>
and vice versa, but without any explicit reference to the other, so you can have as many of these types as you want. with only one specialization.
//special recursive case
template <template<class> class other>
struct Yin<other<void>>
{
other<Yin<void>> *m_ptr;
};
template <template<class> class other>
struct Yang<other<void>>
{
other<Yang<void>> *m_ptr;
};
However, these specializations kick in for any template<void>
type, so we need to apply SFINAE with a type trait:
template<template<class> class T> struct is_yinyang : public std::false_type {};
template<> struct is_yinyang<Yin> : public std::true_type {};
template<> struct is_yinyang<Yang> : public std::true_type {}
Then comes this horrible part, which is absurdly complicated and ugly, and requires a pointless extra template parameter on the Yin/Yang types:
//here's Yin + Specialization
template <typename T, class allowed=void>
struct Yin {
T *m_ptr;
};
template<> struct is_yinyang<Yin> : public std::true_type {};
template <template<class,class> class other>
struct Yin<other<void,void>,typename std::enable_if<is_yinyang<other>::value>::type>
{
other<Yin<void,void>,void> *m_ptr;
};
Now Yin and Yang reference only themselves, and adding new recursive pointer types is trivial. Proof of compilation here: http://coliru.stacked-crooked.com/a/47ecd31e7d48f617
"But wait!" You exclaim, then I have to duplicate all my members! Not quite simply split Yin
into a class with the members that are shared, and have it inherit from Yin_specialmembers<T>
, which contains the members that need specialization. Easy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With