Type System for Layers and Nodes
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
1
down vote
favorite
Overview
The following is a type system of layers (A
, B
, C
, Null
) and nodes for a library.
A version that prints colored text for debugging is on github.
Thanks in advance for reviewing this.
Features
The user can create a stack of layers:
- The type of a stack can be
C<B<A<>>>
,B<C<A<>>>
,B<A>
orC<A>
- The lowest layer of a stack must be a memory layer (e.g.
A
in code).
Conversions between nodes produced by any layers of a stack is possible.
For examaple, consider a stack
C<B<A<>>>
:- The 3 layers of the stack would create 3 kinds of nodes, called
Na
,Nb
,Nc
. Na
,Nb
,Nc
can convert to each other.- The conversion should happen stepwise. For example, conversion
Na
->Nc
involves conversionsNa
->Nb
->Nc
Na
,Nb
,Nc
are respectively instantiation of class templatesNode_a
,Node_b
,Node_c
layer_type::node_type
areNa
,Nb
,Nc
for the 3 layers.
- The 3 layers of the stack would create 3 kinds of nodes, called
Can write functions that only accept instantiation of a node template. This is enabled by std::enable_if_t
and traits in the library.
is_node<T>
checks if type T is a node.converted_node_is_instantiation_of<T, Node_template>
checks if some conversion ofT
is an instantiation of theNode_template
.can_instantiate_to<template, T>
checks ifT
is an instantiaion of thetemplate
.
Use case
- Library of stackable layers with proxies for manipulating the layers.
Problem
The
is_node
trait is not quite extensible.Constraining multiple arguments of a function to accept only instantiation of some node_templates is tedious. Example for constraining just one argument:
template<typename T,
typename=std::enable_if_t<
node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
// convert c to an actual instantiation of Node_c ...
- A possible alternative is a helper that checks with
static_assert
. This helper would also handle type conversion of nodes. But that has other problems as well:
// Possible api
void foo(const T& c)
auto c2 = convert_node<Node_c>(c);
// void foo(const T& b) // can't overload this way.
// auto b2 = convert_node<Node_b>(b);
//
The compilation error message for conversions between node types is not very clear. Whether a conversion between nodes is possible actually depends on the stack of layers. But the stack of layers is not shown in the error message.
Colored error message in run time won't help if the program doesn't compile.
No specification for
node
,const_node
,view_of_node
, andview_of_const_node
.
type_system.cpp
#include <iostream>
#include <type_traits>
// -- [[ Forward declarations ]] -- //
template<class> struct Node_a;
template<class> struct Node_b;
template<class> struct Node_c;
struct Node_null;
template<class, class, int> struct B;
template<class, class> struct C;
struct Null;
// -- [[ Node converters ]] -- //
template<class Self, class Next>
struct Chained_converter
template<class Destination,
class = std::enable_if_t<
std::is_constructible_v<Destination, Next>
&& !std::is_same<Destination, Null>::value
>
>
operator Destination()
return static_cast<Self*>(this)->operator Next();
;
template<class L>
struct Node_converter
: Chained_converter<class L::node_type,
class L::prev_type::node_type>,
Chained_converter<class L::node_type,
class L::next_type::node_type>
;
template<typename Node> using Promote_node = typename
Node::layer_type::next_type::node_type;
template<typename Node> using Demote_node = typename
Node::layer_type::prev_type::node_type;
// -- [[ Node ]] -- //
struct Node_null
using layer_type=Null;
;
template <class Self, class L>
class Node_base : public Node_converter<L>
using next_type = typename L::next_type::node_type;
using prev_type = typename L::prev_type::node_type;
public:
operator prev_type() return prev_type();
operator next_type() return next_type();
;
template<class L>
struct Node_a : public Node_base<Node_a<L>, L>
using layer_type = L;
using Node_base<Node_a<L>, L>::Node_base;
;
template<class L>
struct Node_b : public Node_base<Node_b<L>, L>
using layer_type = L;
using Node_base<Node_b<L>, L>::Node_base;
;
template<class L>
struct Node_c : public Node_base<Node_c<L>, L>
using layer_type = L;
using Node_base<Node_c<L>, L>::Node_base;
;
// -- [[ Layers ]] --
struct Null
using node_type = Node_null;
;
template<class Raw_prev=Null, class Next=Null>
struct A
using self_type = A<Raw_prev, Next>;
using node_type = Node_a<self_type>;
using prev_type = Null;
using next_type = Next;
template<class New_next>
using rebase = A<Raw_prev, New_next>;
;
template<class Raw_prev, class Next=Null, int Parm = 1>
struct B
using self_type = B<Raw_prev, Next, Parm>;
using node_type = Node_b<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next>
using rebase = B<Raw_prev, New_next, Parm>;
;
template<class Raw_prev, class Next=Null>
struct C
using self_type = C<Raw_prev, Next>;
using node_type = Node_c<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next> using rebase = C<Raw_prev, New_next>;
;
// -- [[ Node Traits ]] -- //
template<template<typename...> class, typename...>
struct can_instantiate_to : public std::false_type
;
template<template<typename...> class U, typename... T>
struct can_instantiate_to<U, U<T...>> : public std::true_type
;
template<typename L>
struct is_node
: std::integral_constant<bool,
can_instantiate_to<Node_a, L>::value
|| can_instantiate_to<Node_b, L>::value
|| can_instantiate_to<Node_c, L>::value
> ;
template<typename Node, template<typename...> class Node_template>
constexpr bool is_up_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_up_instantiation_of<
Promote_node<Node>, Node_template>();
template<typename Node, template<typename...> class Node_template>
constexpr bool is_down_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_down_instantiation_of<
Demote_node<Node>, Node_template>();
template<typename T, template<typename...> class Node_template>
constexpr bool converted_node_is_instantiation_of() is_down_instantiation_of<T, Node_template>());
// -- [[ Test ]] -- //
template<typename T,
typename=std::enable_if_t<
converted_node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
int main()
// Create a types for a stack of layers.
using Stack_c = C<B<A<>>>;
using Partial_stack_b = typename Stack_c::prev_type;
using Partial_stack_a = typename Partial_stack_b::prev_type;
typename Stack_c::node_type c;
typename Partial_stack_a::node_type a;
// Test chained type conversions
c = a; // Node_c->b->a
a = c; // Node_a->b->c
// Test constraining parameter of a function
foo(a);
// Create types for 2nd stack of layers.
using Stack2_c = C<A<>>;
using Partial_stack2_a = typename Stack2_c::prev_type;
// Should be false because Stack2_c::node_type is a node
// but it is not an instantiation of the Node_b template.
std::cout << converted_node_is_instantiation_of<
Stack2_c::node_type, Node_b>();
c++ template template-meta-programming c++17 type-safety
add a comment |Â
up vote
1
down vote
favorite
Overview
The following is a type system of layers (A
, B
, C
, Null
) and nodes for a library.
A version that prints colored text for debugging is on github.
Thanks in advance for reviewing this.
Features
The user can create a stack of layers:
- The type of a stack can be
C<B<A<>>>
,B<C<A<>>>
,B<A>
orC<A>
- The lowest layer of a stack must be a memory layer (e.g.
A
in code).
Conversions between nodes produced by any layers of a stack is possible.
For examaple, consider a stack
C<B<A<>>>
:- The 3 layers of the stack would create 3 kinds of nodes, called
Na
,Nb
,Nc
. Na
,Nb
,Nc
can convert to each other.- The conversion should happen stepwise. For example, conversion
Na
->Nc
involves conversionsNa
->Nb
->Nc
Na
,Nb
,Nc
are respectively instantiation of class templatesNode_a
,Node_b
,Node_c
layer_type::node_type
areNa
,Nb
,Nc
for the 3 layers.
- The 3 layers of the stack would create 3 kinds of nodes, called
Can write functions that only accept instantiation of a node template. This is enabled by std::enable_if_t
and traits in the library.
is_node<T>
checks if type T is a node.converted_node_is_instantiation_of<T, Node_template>
checks if some conversion ofT
is an instantiation of theNode_template
.can_instantiate_to<template, T>
checks ifT
is an instantiaion of thetemplate
.
Use case
- Library of stackable layers with proxies for manipulating the layers.
Problem
The
is_node
trait is not quite extensible.Constraining multiple arguments of a function to accept only instantiation of some node_templates is tedious. Example for constraining just one argument:
template<typename T,
typename=std::enable_if_t<
node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
// convert c to an actual instantiation of Node_c ...
- A possible alternative is a helper that checks with
static_assert
. This helper would also handle type conversion of nodes. But that has other problems as well:
// Possible api
void foo(const T& c)
auto c2 = convert_node<Node_c>(c);
// void foo(const T& b) // can't overload this way.
// auto b2 = convert_node<Node_b>(b);
//
The compilation error message for conversions between node types is not very clear. Whether a conversion between nodes is possible actually depends on the stack of layers. But the stack of layers is not shown in the error message.
Colored error message in run time won't help if the program doesn't compile.
No specification for
node
,const_node
,view_of_node
, andview_of_const_node
.
type_system.cpp
#include <iostream>
#include <type_traits>
// -- [[ Forward declarations ]] -- //
template<class> struct Node_a;
template<class> struct Node_b;
template<class> struct Node_c;
struct Node_null;
template<class, class, int> struct B;
template<class, class> struct C;
struct Null;
// -- [[ Node converters ]] -- //
template<class Self, class Next>
struct Chained_converter
template<class Destination,
class = std::enable_if_t<
std::is_constructible_v<Destination, Next>
&& !std::is_same<Destination, Null>::value
>
>
operator Destination()
return static_cast<Self*>(this)->operator Next();
;
template<class L>
struct Node_converter
: Chained_converter<class L::node_type,
class L::prev_type::node_type>,
Chained_converter<class L::node_type,
class L::next_type::node_type>
;
template<typename Node> using Promote_node = typename
Node::layer_type::next_type::node_type;
template<typename Node> using Demote_node = typename
Node::layer_type::prev_type::node_type;
// -- [[ Node ]] -- //
struct Node_null
using layer_type=Null;
;
template <class Self, class L>
class Node_base : public Node_converter<L>
using next_type = typename L::next_type::node_type;
using prev_type = typename L::prev_type::node_type;
public:
operator prev_type() return prev_type();
operator next_type() return next_type();
;
template<class L>
struct Node_a : public Node_base<Node_a<L>, L>
using layer_type = L;
using Node_base<Node_a<L>, L>::Node_base;
;
template<class L>
struct Node_b : public Node_base<Node_b<L>, L>
using layer_type = L;
using Node_base<Node_b<L>, L>::Node_base;
;
template<class L>
struct Node_c : public Node_base<Node_c<L>, L>
using layer_type = L;
using Node_base<Node_c<L>, L>::Node_base;
;
// -- [[ Layers ]] --
struct Null
using node_type = Node_null;
;
template<class Raw_prev=Null, class Next=Null>
struct A
using self_type = A<Raw_prev, Next>;
using node_type = Node_a<self_type>;
using prev_type = Null;
using next_type = Next;
template<class New_next>
using rebase = A<Raw_prev, New_next>;
;
template<class Raw_prev, class Next=Null, int Parm = 1>
struct B
using self_type = B<Raw_prev, Next, Parm>;
using node_type = Node_b<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next>
using rebase = B<Raw_prev, New_next, Parm>;
;
template<class Raw_prev, class Next=Null>
struct C
using self_type = C<Raw_prev, Next>;
using node_type = Node_c<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next> using rebase = C<Raw_prev, New_next>;
;
// -- [[ Node Traits ]] -- //
template<template<typename...> class, typename...>
struct can_instantiate_to : public std::false_type
;
template<template<typename...> class U, typename... T>
struct can_instantiate_to<U, U<T...>> : public std::true_type
;
template<typename L>
struct is_node
: std::integral_constant<bool,
can_instantiate_to<Node_a, L>::value
|| can_instantiate_to<Node_b, L>::value
|| can_instantiate_to<Node_c, L>::value
> ;
template<typename Node, template<typename...> class Node_template>
constexpr bool is_up_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_up_instantiation_of<
Promote_node<Node>, Node_template>();
template<typename Node, template<typename...> class Node_template>
constexpr bool is_down_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_down_instantiation_of<
Demote_node<Node>, Node_template>();
template<typename T, template<typename...> class Node_template>
constexpr bool converted_node_is_instantiation_of() is_down_instantiation_of<T, Node_template>());
// -- [[ Test ]] -- //
template<typename T,
typename=std::enable_if_t<
converted_node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
int main()
// Create a types for a stack of layers.
using Stack_c = C<B<A<>>>;
using Partial_stack_b = typename Stack_c::prev_type;
using Partial_stack_a = typename Partial_stack_b::prev_type;
typename Stack_c::node_type c;
typename Partial_stack_a::node_type a;
// Test chained type conversions
c = a; // Node_c->b->a
a = c; // Node_a->b->c
// Test constraining parameter of a function
foo(a);
// Create types for 2nd stack of layers.
using Stack2_c = C<A<>>;
using Partial_stack2_a = typename Stack2_c::prev_type;
// Should be false because Stack2_c::node_type is a node
// but it is not an instantiation of the Node_b template.
std::cout << converted_node_is_instantiation_of<
Stack2_c::node_type, Node_b>();
c++ template template-meta-programming c++17 type-safety
How much can concepts simplify this? I haven't looked up how to use concepts yet.
â R zu
May 14 at 0:45
add a comment |Â
up vote
1
down vote
favorite
up vote
1
down vote
favorite
Overview
The following is a type system of layers (A
, B
, C
, Null
) and nodes for a library.
A version that prints colored text for debugging is on github.
Thanks in advance for reviewing this.
Features
The user can create a stack of layers:
- The type of a stack can be
C<B<A<>>>
,B<C<A<>>>
,B<A>
orC<A>
- The lowest layer of a stack must be a memory layer (e.g.
A
in code).
Conversions between nodes produced by any layers of a stack is possible.
For examaple, consider a stack
C<B<A<>>>
:- The 3 layers of the stack would create 3 kinds of nodes, called
Na
,Nb
,Nc
. Na
,Nb
,Nc
can convert to each other.- The conversion should happen stepwise. For example, conversion
Na
->Nc
involves conversionsNa
->Nb
->Nc
Na
,Nb
,Nc
are respectively instantiation of class templatesNode_a
,Node_b
,Node_c
layer_type::node_type
areNa
,Nb
,Nc
for the 3 layers.
- The 3 layers of the stack would create 3 kinds of nodes, called
Can write functions that only accept instantiation of a node template. This is enabled by std::enable_if_t
and traits in the library.
is_node<T>
checks if type T is a node.converted_node_is_instantiation_of<T, Node_template>
checks if some conversion ofT
is an instantiation of theNode_template
.can_instantiate_to<template, T>
checks ifT
is an instantiaion of thetemplate
.
Use case
- Library of stackable layers with proxies for manipulating the layers.
Problem
The
is_node
trait is not quite extensible.Constraining multiple arguments of a function to accept only instantiation of some node_templates is tedious. Example for constraining just one argument:
template<typename T,
typename=std::enable_if_t<
node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
// convert c to an actual instantiation of Node_c ...
- A possible alternative is a helper that checks with
static_assert
. This helper would also handle type conversion of nodes. But that has other problems as well:
// Possible api
void foo(const T& c)
auto c2 = convert_node<Node_c>(c);
// void foo(const T& b) // can't overload this way.
// auto b2 = convert_node<Node_b>(b);
//
The compilation error message for conversions between node types is not very clear. Whether a conversion between nodes is possible actually depends on the stack of layers. But the stack of layers is not shown in the error message.
Colored error message in run time won't help if the program doesn't compile.
No specification for
node
,const_node
,view_of_node
, andview_of_const_node
.
type_system.cpp
#include <iostream>
#include <type_traits>
// -- [[ Forward declarations ]] -- //
template<class> struct Node_a;
template<class> struct Node_b;
template<class> struct Node_c;
struct Node_null;
template<class, class, int> struct B;
template<class, class> struct C;
struct Null;
// -- [[ Node converters ]] -- //
template<class Self, class Next>
struct Chained_converter
template<class Destination,
class = std::enable_if_t<
std::is_constructible_v<Destination, Next>
&& !std::is_same<Destination, Null>::value
>
>
operator Destination()
return static_cast<Self*>(this)->operator Next();
;
template<class L>
struct Node_converter
: Chained_converter<class L::node_type,
class L::prev_type::node_type>,
Chained_converter<class L::node_type,
class L::next_type::node_type>
;
template<typename Node> using Promote_node = typename
Node::layer_type::next_type::node_type;
template<typename Node> using Demote_node = typename
Node::layer_type::prev_type::node_type;
// -- [[ Node ]] -- //
struct Node_null
using layer_type=Null;
;
template <class Self, class L>
class Node_base : public Node_converter<L>
using next_type = typename L::next_type::node_type;
using prev_type = typename L::prev_type::node_type;
public:
operator prev_type() return prev_type();
operator next_type() return next_type();
;
template<class L>
struct Node_a : public Node_base<Node_a<L>, L>
using layer_type = L;
using Node_base<Node_a<L>, L>::Node_base;
;
template<class L>
struct Node_b : public Node_base<Node_b<L>, L>
using layer_type = L;
using Node_base<Node_b<L>, L>::Node_base;
;
template<class L>
struct Node_c : public Node_base<Node_c<L>, L>
using layer_type = L;
using Node_base<Node_c<L>, L>::Node_base;
;
// -- [[ Layers ]] --
struct Null
using node_type = Node_null;
;
template<class Raw_prev=Null, class Next=Null>
struct A
using self_type = A<Raw_prev, Next>;
using node_type = Node_a<self_type>;
using prev_type = Null;
using next_type = Next;
template<class New_next>
using rebase = A<Raw_prev, New_next>;
;
template<class Raw_prev, class Next=Null, int Parm = 1>
struct B
using self_type = B<Raw_prev, Next, Parm>;
using node_type = Node_b<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next>
using rebase = B<Raw_prev, New_next, Parm>;
;
template<class Raw_prev, class Next=Null>
struct C
using self_type = C<Raw_prev, Next>;
using node_type = Node_c<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next> using rebase = C<Raw_prev, New_next>;
;
// -- [[ Node Traits ]] -- //
template<template<typename...> class, typename...>
struct can_instantiate_to : public std::false_type
;
template<template<typename...> class U, typename... T>
struct can_instantiate_to<U, U<T...>> : public std::true_type
;
template<typename L>
struct is_node
: std::integral_constant<bool,
can_instantiate_to<Node_a, L>::value
|| can_instantiate_to<Node_b, L>::value
|| can_instantiate_to<Node_c, L>::value
> ;
template<typename Node, template<typename...> class Node_template>
constexpr bool is_up_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_up_instantiation_of<
Promote_node<Node>, Node_template>();
template<typename Node, template<typename...> class Node_template>
constexpr bool is_down_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_down_instantiation_of<
Demote_node<Node>, Node_template>();
template<typename T, template<typename...> class Node_template>
constexpr bool converted_node_is_instantiation_of() is_down_instantiation_of<T, Node_template>());
// -- [[ Test ]] -- //
template<typename T,
typename=std::enable_if_t<
converted_node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
int main()
// Create a types for a stack of layers.
using Stack_c = C<B<A<>>>;
using Partial_stack_b = typename Stack_c::prev_type;
using Partial_stack_a = typename Partial_stack_b::prev_type;
typename Stack_c::node_type c;
typename Partial_stack_a::node_type a;
// Test chained type conversions
c = a; // Node_c->b->a
a = c; // Node_a->b->c
// Test constraining parameter of a function
foo(a);
// Create types for 2nd stack of layers.
using Stack2_c = C<A<>>;
using Partial_stack2_a = typename Stack2_c::prev_type;
// Should be false because Stack2_c::node_type is a node
// but it is not an instantiation of the Node_b template.
std::cout << converted_node_is_instantiation_of<
Stack2_c::node_type, Node_b>();
c++ template template-meta-programming c++17 type-safety
Overview
The following is a type system of layers (A
, B
, C
, Null
) and nodes for a library.
A version that prints colored text for debugging is on github.
Thanks in advance for reviewing this.
Features
The user can create a stack of layers:
- The type of a stack can be
C<B<A<>>>
,B<C<A<>>>
,B<A>
orC<A>
- The lowest layer of a stack must be a memory layer (e.g.
A
in code).
Conversions between nodes produced by any layers of a stack is possible.
For examaple, consider a stack
C<B<A<>>>
:- The 3 layers of the stack would create 3 kinds of nodes, called
Na
,Nb
,Nc
. Na
,Nb
,Nc
can convert to each other.- The conversion should happen stepwise. For example, conversion
Na
->Nc
involves conversionsNa
->Nb
->Nc
Na
,Nb
,Nc
are respectively instantiation of class templatesNode_a
,Node_b
,Node_c
layer_type::node_type
areNa
,Nb
,Nc
for the 3 layers.
- The 3 layers of the stack would create 3 kinds of nodes, called
Can write functions that only accept instantiation of a node template. This is enabled by std::enable_if_t
and traits in the library.
is_node<T>
checks if type T is a node.converted_node_is_instantiation_of<T, Node_template>
checks if some conversion ofT
is an instantiation of theNode_template
.can_instantiate_to<template, T>
checks ifT
is an instantiaion of thetemplate
.
Use case
- Library of stackable layers with proxies for manipulating the layers.
Problem
The
is_node
trait is not quite extensible.Constraining multiple arguments of a function to accept only instantiation of some node_templates is tedious. Example for constraining just one argument:
template<typename T,
typename=std::enable_if_t<
node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
// convert c to an actual instantiation of Node_c ...
- A possible alternative is a helper that checks with
static_assert
. This helper would also handle type conversion of nodes. But that has other problems as well:
// Possible api
void foo(const T& c)
auto c2 = convert_node<Node_c>(c);
// void foo(const T& b) // can't overload this way.
// auto b2 = convert_node<Node_b>(b);
//
The compilation error message for conversions between node types is not very clear. Whether a conversion between nodes is possible actually depends on the stack of layers. But the stack of layers is not shown in the error message.
Colored error message in run time won't help if the program doesn't compile.
No specification for
node
,const_node
,view_of_node
, andview_of_const_node
.
type_system.cpp
#include <iostream>
#include <type_traits>
// -- [[ Forward declarations ]] -- //
template<class> struct Node_a;
template<class> struct Node_b;
template<class> struct Node_c;
struct Node_null;
template<class, class, int> struct B;
template<class, class> struct C;
struct Null;
// -- [[ Node converters ]] -- //
template<class Self, class Next>
struct Chained_converter
template<class Destination,
class = std::enable_if_t<
std::is_constructible_v<Destination, Next>
&& !std::is_same<Destination, Null>::value
>
>
operator Destination()
return static_cast<Self*>(this)->operator Next();
;
template<class L>
struct Node_converter
: Chained_converter<class L::node_type,
class L::prev_type::node_type>,
Chained_converter<class L::node_type,
class L::next_type::node_type>
;
template<typename Node> using Promote_node = typename
Node::layer_type::next_type::node_type;
template<typename Node> using Demote_node = typename
Node::layer_type::prev_type::node_type;
// -- [[ Node ]] -- //
struct Node_null
using layer_type=Null;
;
template <class Self, class L>
class Node_base : public Node_converter<L>
using next_type = typename L::next_type::node_type;
using prev_type = typename L::prev_type::node_type;
public:
operator prev_type() return prev_type();
operator next_type() return next_type();
;
template<class L>
struct Node_a : public Node_base<Node_a<L>, L>
using layer_type = L;
using Node_base<Node_a<L>, L>::Node_base;
;
template<class L>
struct Node_b : public Node_base<Node_b<L>, L>
using layer_type = L;
using Node_base<Node_b<L>, L>::Node_base;
;
template<class L>
struct Node_c : public Node_base<Node_c<L>, L>
using layer_type = L;
using Node_base<Node_c<L>, L>::Node_base;
;
// -- [[ Layers ]] --
struct Null
using node_type = Node_null;
;
template<class Raw_prev=Null, class Next=Null>
struct A
using self_type = A<Raw_prev, Next>;
using node_type = Node_a<self_type>;
using prev_type = Null;
using next_type = Next;
template<class New_next>
using rebase = A<Raw_prev, New_next>;
;
template<class Raw_prev, class Next=Null, int Parm = 1>
struct B
using self_type = B<Raw_prev, Next, Parm>;
using node_type = Node_b<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next>
using rebase = B<Raw_prev, New_next, Parm>;
;
template<class Raw_prev, class Next=Null>
struct C
using self_type = C<Raw_prev, Next>;
using node_type = Node_c<self_type>;
using prev_type = typename Raw_prev::template rebase<self_type>;
using next_type = Next;
template<class New_next> using rebase = C<Raw_prev, New_next>;
;
// -- [[ Node Traits ]] -- //
template<template<typename...> class, typename...>
struct can_instantiate_to : public std::false_type
;
template<template<typename...> class U, typename... T>
struct can_instantiate_to<U, U<T...>> : public std::true_type
;
template<typename L>
struct is_node
: std::integral_constant<bool,
can_instantiate_to<Node_a, L>::value
|| can_instantiate_to<Node_b, L>::value
|| can_instantiate_to<Node_c, L>::value
> ;
template<typename Node, template<typename...> class Node_template>
constexpr bool is_up_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_up_instantiation_of<
Promote_node<Node>, Node_template>();
template<typename Node, template<typename...> class Node_template>
constexpr bool is_down_instantiation_of()
if constexpr (std::is_same<Node, Node_null>::value)
return false;
else
if constexpr (can_instantiate_to<Node_template, Node>::value)
return true;
else
return is_down_instantiation_of<
Demote_node<Node>, Node_template>();
template<typename T, template<typename...> class Node_template>
constexpr bool converted_node_is_instantiation_of() is_down_instantiation_of<T, Node_template>());
// -- [[ Test ]] -- //
template<typename T,
typename=std::enable_if_t<
converted_node_is_instantiation_of<T, Node_c>()> >
void foo(const T& c)
int main()
// Create a types for a stack of layers.
using Stack_c = C<B<A<>>>;
using Partial_stack_b = typename Stack_c::prev_type;
using Partial_stack_a = typename Partial_stack_b::prev_type;
typename Stack_c::node_type c;
typename Partial_stack_a::node_type a;
// Test chained type conversions
c = a; // Node_c->b->a
a = c; // Node_a->b->c
// Test constraining parameter of a function
foo(a);
// Create types for 2nd stack of layers.
using Stack2_c = C<A<>>;
using Partial_stack2_a = typename Stack2_c::prev_type;
// Should be false because Stack2_c::node_type is a node
// but it is not an instantiation of the Node_b template.
std::cout << converted_node_is_instantiation_of<
Stack2_c::node_type, Node_b>();
c++ template template-meta-programming c++17 type-safety
edited May 14 at 0:40
asked May 13 at 22:18
R zu
2026
2026
How much can concepts simplify this? I haven't looked up how to use concepts yet.
â R zu
May 14 at 0:45
add a comment |Â
How much can concepts simplify this? I haven't looked up how to use concepts yet.
â R zu
May 14 at 0:45
How much can concepts simplify this? I haven't looked up how to use concepts yet.
â R zu
May 14 at 0:45
How much can concepts simplify this? I haven't looked up how to use concepts yet.
â R zu
May 14 at 0:45
add a comment |Â
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f194326%2ftype-system-for-layers-and-nodes%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
How much can concepts simplify this? I haven't looked up how to use concepts yet.
â R zu
May 14 at 0:45