First off my use case, as I may think in the wrong direction: I want to create a map that maps a value to types. So for example:
Map<std::string> map;
map.insert<int, double, char>("Hey");
auto string = map.at<int, double, char>();
This alone is fairly easy to do with std::type_index. However, I want to add the possibility to match types that are not exact the searched ones, when they are convertible. So the following should also return "Hey", as float can be converted to double:
auto string = map.at<int, float, char>();
I can't use type_index for this case as std::is_convertible only works directly on types. This would be the version without conversion, but as far as it seems it's not easily possible to add conversion handling into it without major changes.
My current attempt looks kind of like the following, please note that this is not working and just shows what I have tried to implement:
template<typename T>
class Map {
T value;
std::vector<Map<T>> children; // all the children of the current node.
// in the above example, if this was
// the int node, the only child
// would be the double node
template<typename T1>
constexpr bool is_convertible() const {
return std::is_convertible<__T__, T1>::value; // this isn't applicable
// since __T__ can't be
// stored (this nodes
// type)
}
public:
template<typename T1, typename... Tn>
void insert(T&& value) {
// iterate through/create the child nodes until the last template param
}
template<typename T1, typename... Tn>
T& at() {
// iterate through thechild nodes until a matching child is found
// either exact match or a convertible
for(auto &c: children) {
// if the above function would work
if(c.template is_convertible<T1>()) {
return c.template at<Tn...>();
}
}
}
}
Now I'm at my wits end how to achieve this. I thought of implementing lambdas as comparator functions, but while the lambda can store the type of the current node, it can't accept a template parameter on call to compare to.
Is there some C+1y generic lambda comparator magic, or even an easier way?
I hope this does what you want, there's ample space for extension and for creating template specialization that attach to any type combination you want. It's not super-pretty, but it can probably be refactored a bit and beautified.
#include <iostream>
template <typename... Args>
struct map {
};
template <>
struct map<int, float, char> {
static constexpr char value[] = "int float char";
};
constexpr char map<int,float,char>::value[];
template <typename T>
struct map<int, T> {
static constexpr typename std::enable_if<std::is_integral<T>::value, char>::type value[] = "int, T";
};
template <typename T>
constexpr typename std::enable_if<std::is_integral<T>::value, char>::type map<int,T>::value[];
int main() {
std::string v = map<int,float,char>::value;
std::string w = map<int,int>::value;
std::string w2 = map<int,unsigned>::value;
// std::string w3 = map<int,float>::value; Won't compile
std::cout << v << "\n";
std::cout << w << "\n";
std::cout << w2 << "\n";
return 0;
}
I wrote some weird code using boost::fusion that comes close to doing what you want:
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/include/insert.hpp>
#include <boost/fusion/include/pair.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <string>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <memory>
template <std::size_t Value1, std::size_t Value2>
struct MinSizeT {
static const std::size_t value = (Value1 > Value2) ? Value2 : Value1;
};
template<typename T1, typename T2, std::size_t N>
struct TupleIsConvertibleHelper {
static const bool value = std::is_convertible<typename std::tuple_element<N - 1, T1>::type, typename std::tuple_element<N - 1, T2>::type>::value && TupleIsConvertibleHelper<T1, T2, N - 1>::value;
};
template<typename T1, typename T2>
struct TupleIsConvertibleHelper<T1, T2, 0> {
static const bool value = true;
};
template<typename T1, typename T2>
bool TupleIsConvertible() { // Return true if all types in T1 are convertible to their corresponding type in T2
if (std::tuple_size<T1>::value != std::tuple_size<T2>::value)
return false;
constexpr std::size_t minSize = MinSizeT<std::tuple_size<T1>::value, std::tuple_size<T2>::value>::value;
return TupleIsConvertibleHelper<T1, T2, minSize>::value;
}
template<typename MapInserter>
class Map {
MapInserter mc;
template<typename... Types>
struct do_at {
template <typename T>
void operator()(T const& x) const { // Find an exact match or the last convertible match
typedef std::tuple<Types...> t1;
typedef typename T::first_type t2;
if (exactMatch)
return;
if (std::is_same<t1, t2>::value) {
exactMatch = true;
value = x.second;
}
else if (TupleIsConvertible<t1, t2>())
value = x.second;
}
mutable bool exactMatch;
mutable typename MapInserter::value_type value;
do_at() : exactMatch(false) {}
};
public:
Map(MapInserter _mc) : mc(_mc) { }
template<typename... Types>
typename MapInserter::value_type at() {
do_at<Types...> res;
boost::fusion::for_each(mc.data->map, res);
return res.value;
}
};
template<typename ValueType, typename MapType = boost::fusion::map<>, typename ParentType = void*>
struct MapInserter {
typedef ValueType value_type;
struct Helper {
MapType map;
std::shared_ptr<ParentType> parent; // Must keep parent alive because fusion is lazy.
Helper() = default;
Helper(MapType&& _map, std::shared_ptr<ParentType> _parent) : map(std::move(_map)), parent(_parent) {}
};
std::shared_ptr<Helper> data;
template<typename... KeyTypes>
auto Insert(ValueType value) -> MapInserter<ValueType, decltype(boost::fusion::insert(data->map, boost::fusion::end(data->map), boost::fusion::make_pair<std::tuple<KeyTypes...>>(value))), Helper> {
auto newMap = boost::fusion::insert(data->map, boost::fusion::end(data->map), boost::fusion::make_pair<std::tuple<KeyTypes...>>(value));
return MapInserter<ValueType, decltype(newMap), Helper>(std::move(newMap), data);
}
MapInserter() : data(std::make_shared<Helper>()) { }
MapInserter(MapType&& _map, std::shared_ptr<ParentType> _parent) : data(std::make_shared<Helper>(std::move(_map), _parent)) {}
MapInserter(MapInserter&&) = default;
MapInserter(const MapInserter&) = default;
};
int main() {
auto mc = MapInserter<std::string>().
Insert<int, char, float>("***int, char, float***").
Insert<float, double>("***float, double***").
Insert<int>("***int***").
Insert<unsigned, bool>("***unsigned, bool***");
Map<decltype(mc)> map(mc);
std::cout << map.at<int, char, float>() << std::endl; // "***int, char, float***"
std::cout << map.at<int, char, double>() << std::endl; // "***int, char, float***"
std::cout << map.at<char>() << std::endl; // "***int***"
return 0;
}
template<class...>struct types { typedef types type; };
template<class T, class types>struct type_index;
template<class T, class...Ts>
struct type_index<T,types<T, Ts...>>:
std::integral_constant<unsigned,0>
{};
template<class T, class T0, class...Ts>
struct type_index<T,types<T0, Ts...>>:
std::integral_constant<unsigned,type_index<T,types<Ts...>::value+1>
{};
template<template<class>class filter, class types_in, class types_out=types<>, class details=void>
struct filter;
template<template<class>class filter, class T0, class... Ts, class... Zs>
struct filter<filter, types<T0,types...>, types<Zs...>,
typename std::enable_if< filter<T0>::value >::type
>: filter<filter, types<types...>, types<Zs...,T0>>
{};
template<template<class>class filter, class T0, class... Ts, class... Zs>
struct filter<filter, types<T0,types...>, types<Zs...>,
typename std::enable_if< !filter<T0>::value >::type
>: filter<filter, types<types...>, types<Zs...>>
{};
template<template<class>class filter, class... Zs>
struct filter<filter, types<>, types<Zs...>,
void
>: types<Zs...>
{};
template<typename T>
struct convertable_to_test {
template<typename U>
using test = std::is_convertible<U, T>;
};
template<class T, class types>
struct get_convertable_to_types:filter< convertable_to_test<T>::template test, types> {};
which is a start.
Create a master types<Ts...> of all of the types your system supports. Call this SupportedTypes.
Map types<Ts...> to std::vector<unsigned> of each type offset in the above list. Now you can store a collection of types at runtime. Call this a runtime type vector.
When adding an entry types<Args...> to the map, run get_convertable_to_types on each type in types<Args...>, and build a cross product in types< types<...>... >. Store the resulting exponential number of runtime type vectors in your implementation details map.
When you query with types<Ts...>, conver to the runtime type vector, and look it up in the implementation details map. And done!
An alternative approach would be to write get_convertable_from_types, and do the mapping to an exponential number of types<Ts...> at the query point, convert each to a runtime type vector. When adding stuff to the map, store only one runtime type vector. This has slower lookup performance, but faster setup performance, and uses far less memory.
I was going to finish this, but got busy.
I got it into my head fairly recently that I would attempt to create a tree of 'lists'. That is, a tree where each level is a list, so it's not a binary tree. Furthermore, I wanted to try to make each level of the tree a different type, specifically four different types - one for each level. Lastly, I intended to see if I could, at compile-time, fix the height of the tree by employing three different templates.
tree_middle, for the the intermediate levels of the tree,
template<typename a, typename b, typename c>
struct tree_middle
{
tree_middle *m_prev;
tree_middle *m_next;
a *m_upper;
b *m_node;
c *m_lower;
};
tree_bottom, for the bottom of the tree,
template<typename a, typename b>
struct tree_bottom
{
tree_bottom *m_prev;
tree_bottom *m_next;
a *m_upper;
b *m_node;
};
and tree_top for the top of the tree.
template<typename a, typename b>
struct tree_top
{
tree_top *m_prev;
tree_top *m_next;
a *m_node;
b *m_lower;
};
After toying around with different implementations, I basically resorted to some workarounds wherein I had a type that denoted the penultimate tree level:
template<typename a, typename b, typename c>
struct tree_prebottom
{
tree_prebottom *m_prev;
tree_prebottom *m_next;
a *m_upper;
b *m_node;
tree_bottom<tree_prebottom, c> *m_lower;
};
By defining yet another template, I could create a tree that was fixed at three levels with three different types. Notice that three_tree functions as tree_top in this template. This is close to what I wanted.
template<typename a, typename b, typename c>
struct three_tree
{
three_tree *m_prev;
three_tree *m_next;
a *m_node;
tree_prebottom<three_tree, b, c> *m_lower;
};
Taking that one step further, I ended up with a template that could generate the type that I was looking for, the four_tree. But notice this ludicrous display going on here? I am writing 'generic' code in a rather loose sense here, agreed? The only thing generic about it at are the consumed types, really. Note: This part was edited when I noticed that the four_tree had no proper link back to the top level.)
template<typename a, typename b, typename c, typename d>
struct tree_threebottom
{
tree_threebottom *m_prev;
tree_threebottom *m_next;
a *m_upper;
b *m_node;
tree_prebottom<tree_threebottom, c, d> *m_lower;
};
template<typename a, typename b, typename c, typename d>
struct four_tree
{
four_tree *m_prev;
four_tree *m_next;
a *m_node;
tree_threebottom<four_tree, b, c, d> *m_lower;
};
The question is, is there a better and more elegant way to do this? The roadblock I ran into when trying to do the original implementation was that when you're specifying type inputs for a template, you can't pass the type you're 'in' at the moment as a parameter. Thus, my approach suffered from never being able to create a complete type due to a sort of cyclic dependency. Even the two level tree suffers from this if you just limit yourself to tree_top and tree_bottom:
template<typename a, typename b>
struct tree_bottom
{
tree_bottom *m_prev;
tree_bottom *m_next;
a *m_upper;
b *m_node;
};
template<typename a, typename b>
struct tree_top
{
tree_top *m_prev;
tree_top *m_next;
a *m_node;
b *m_lower;
};
The templates are fine on their own, until you try to define an actual type with them. For example
typedef tree_top< int, tree_bottom<tree_top<int, tree_bottom< /*see the problem?*/, short> > int_short_tree;
Note that the tree implementation is pretty simplistic, but I was looking to emulate a tree template I found here: http://archive.gamedev.net/archive/reference/programming/features/coretree2/index.html I have also seen similar implementations elsewhere, but they all assume a tree composed of a single type. The natural response to this might be, "Well why not use polymorphism?". I have seen that technique in action as well, such as in the LLVM project, and while I don't have any problem with it, I was curious to know if I could statically (at compile time) construct a type that subverts the need for polymorphism, since in my particular case I knew all of the types involved, and I knew that the tree had a fixed height (four).
I also contemplated using inheritance combined with templates to achieve a more robust solution, but the solution has eluded me, if it exists. It seems to me that I could manually create types of this sort, including trees with 5 levels or more. Am I hitting a limitation of the template system here, or just not being clever enough?
I think I have an idea of what you want and how you could achieve it. It doesn't fit well the SO format, though, I think.
First, the syntax to create & use the tree:
int main()
{
// We want a tree with 4 levels.
// The node type of the 0th level should be `int`,
// of the 1st level `double` and so on.
// (0th level = tree root)
// And we initialize the root node with the `int` 42.
auto my_tree = make_tree_root < int, double, char, float, int > (42);
// add children and navigate through the tree
my_tree.add_child(1.23);
my_tree.add_child(4.56);
my_tree.get_child(1).add_child('x');
my_tree.get_child(1).get_child(0).add_child(1.2f);
my_tree.print();
}
And now the mess in the background. Note that it is rather a proof-of-concept, it has so many flaws that having no comments may be a bless. Especially the multiple inheritance used to reduce code copying creates more problems than it solves.
#include <cstddef>
#include <iostream>
#include <vector>
template < typename... TP >
struct type_vector
{
private:
template < std::size_t t_count, typename TT, typename... TTP >
struct access_elem { using value = typename access_elem < t_count-1, TTP... > :: value; };
template < typename TT, typename... TTP >
struct access_elem < 0, TT, TTP... > { using value = TT; };
public:
template < std::size_t t_id >
using elem = typename access_elem < t_id, TP... > :: value;
};
template < typename, std::size_t, std::size_t >
struct tree_node;
template < typename T_type_vector, std::size_t t_level, std::size_t t_maxLevel >
struct tree_all_base
{
using node = typename T_type_vector::template elem < t_level >;
protected:
node m_node;
public:
explicit tree_all_base(node p) : m_node(p) {}
void change_node(node);
node get_node() const { return m_node; }
void print() const
{
std::cout << "node: " << m_node << std::endl;
}
};
template < typename T_type_vector, std::size_t t_level, std::size_t t_maxLevel >
struct tree_down_base
{
using child = tree_node < T_type_vector, t_level+1, t_maxLevel >;
private:
std::vector<child> m_children;
public:
void add_child(typename child::node p)
{
using derived_type = tree_node < T_type_vector, t_level, t_maxLevel >;
m_children.push_back( child{p, static_cast<derived_type*>(this)} );
}
child const& get_child(std::size_t id) const { return m_children.at(id); }
child& get_child(std::size_t id) { return m_children.at(id); }
// further methods like `remove_child` etc
protected:
void print() const
{
std::cout << "children: ";
for(child const& c : m_children)
{
std::cout << c.get_node() << ", ";
}
std::cout << std::endl;
for(child const& c : m_children)
{
c.print();
}
std::cout << std::endl;
}
};
template < typename T_type_vector, std::size_t t_level, std::size_t t_maxLevel >
struct tree_up_base
: public tree_all_base < T_type_vector, t_level, t_maxLevel >
{
using tree_all_base_ = tree_all_base<T_type_vector,t_level,t_maxLevel>;
using parent = tree_node < T_type_vector, t_level-1, t_maxLevel >;
using node = typename tree_all_base_::node;
protected:
parent* m_parent;
tree_up_base(node p_node, parent* p)
: tree_all_base_(p_node), m_parent(p)
{}
};
template < typename T_type_vector, std::size_t t_level, std::size_t t_maxLevel >
struct tree_node
: public tree_up_base <T_type_vector, t_level, t_maxLevel>
, public tree_down_base<T_type_vector, t_level, t_maxLevel>
{
using node = typename tree_all_base<T_type_vector,t_level,t_maxLevel>::node;
private:
/* inherit ctor....*/
using tree_up_base_ = tree_up_base<T_type_vector,t_level,t_maxLevel>;
using tree_down_base_ = tree_down_base<T_type_vector,t_level,t_maxLevel>;
using tree_node_parent = tree_node<T_type_vector,t_level-1,t_maxLevel>;
friend struct tree_down_base < T_type_vector, t_level-1, t_maxLevel >;
tree_node(node p, tree_node_parent* pb) : tree_up_base_(p, pb) {}
public:
void print() const
{
tree_up_base_::print();
tree_down_base_::print();
}
};
// tree root specialization
template < typename T_type_vector, std::size_t t_maxLevel >
struct tree_node < T_type_vector, 0, t_maxLevel >
: public tree_all_base <T_type_vector, 0, t_maxLevel>
, public tree_down_base<T_type_vector, 0, t_maxLevel>
{
public:
/* inherit ctor..... */
using tree_all_base_ = tree_all_base<T_type_vector,0,t_maxLevel>;
using tree_down_base_ = tree_down_base<T_type_vector,0,t_maxLevel>;
using node = typename tree_all_base_ :: node;
tree_node(node p) : tree_all_base_(p) {}
public:
void print() const
{
tree_all_base_::print();
tree_down_base_::print();
}
};
// tree leaf specialization
template < typename T_type_vector, std::size_t t_maxLevel >
struct tree_node < T_type_vector, t_maxLevel, t_maxLevel >
: public tree_up_base <T_type_vector, t_maxLevel, t_maxLevel>
{
private:
/* inherit ctor.... */
using tree_up_base_ = tree_up_base<T_type_vector,t_maxLevel,t_maxLevel>;
using node = typename tree_up_base_ :: node;
using tree_node_parent = tree_node<T_type_vector,t_maxLevel-1,t_maxLevel>;
friend struct tree_down_base < T_type_vector, t_maxLevel-1, t_maxLevel >;
tree_node(node p, tree_node_parent* pb) : tree_up_base_(p, pb) {}
};
template < typename... TP >
tree_node < type_vector<TP...>, 0, sizeof...(TP)-1 >
make_tree_root(typename type_vector<TP...>::template elem<0> node)
{ return {node}; }
I'm told that the template system in C++ is Turing-complete at compile time. This is mentioned in this post and also on wikipedia.
Can you provide a nontrivial example of a computation that exploits this property?
Is this fact useful in practice?
I've done a turing machine in C++11. Features that C++11 adds are not significant for the turing machine indeed. It just provides for arbitrary length rule lists using variadic templates, instead of using perverse macro metaprogramming :). The names for the conditions are used to output a diagram on stdout. i've removed that code to keep the sample short.
#include <iostream>
template<bool C, typename A, typename B>
struct Conditional {
typedef A type;
};
template<typename A, typename B>
struct Conditional<false, A, B> {
typedef B type;
};
template<typename...>
struct ParameterPack;
template<bool C, typename = void>
struct EnableIf { };
template<typename Type>
struct EnableIf<true, Type> {
typedef Type type;
};
template<typename T>
struct Identity {
typedef T type;
};
// define a type list
template<typename...>
struct TypeList;
template<typename T, typename... TT>
struct TypeList<T, TT...> {
typedef T type;
typedef TypeList<TT...> tail;
};
template<>
struct TypeList<> {
};
template<typename List>
struct GetSize;
template<typename... Items>
struct GetSize<TypeList<Items...>> {
enum { value = sizeof...(Items) };
};
template<typename... T>
struct ConcatList;
template<typename... First, typename... Second, typename... Tail>
struct ConcatList<TypeList<First...>, TypeList<Second...>, Tail...> {
typedef typename ConcatList<TypeList<First..., Second...>,
Tail...>::type type;
};
template<typename T>
struct ConcatList<T> {
typedef T type;
};
template<typename NewItem, typename List>
struct AppendItem;
template<typename NewItem, typename...Items>
struct AppendItem<NewItem, TypeList<Items...>> {
typedef TypeList<Items..., NewItem> type;
};
template<typename NewItem, typename List>
struct PrependItem;
template<typename NewItem, typename...Items>
struct PrependItem<NewItem, TypeList<Items...>> {
typedef TypeList<NewItem, Items...> type;
};
template<typename List, int N, typename = void>
struct GetItem {
static_assert(N > 0, "index cannot be negative");
static_assert(GetSize<List>::value > 0, "index too high");
typedef typename GetItem<typename List::tail, N-1>::type type;
};
template<typename List>
struct GetItem<List, 0> {
static_assert(GetSize<List>::value > 0, "index too high");
typedef typename List::type type;
};
template<typename List, template<typename, typename...> class Matcher, typename... Keys>
struct FindItem {
static_assert(GetSize<List>::value > 0, "Could not match any item.");
typedef typename List::type current_type;
typedef typename Conditional<Matcher<current_type, Keys...>::value,
Identity<current_type>, // found!
FindItem<typename List::tail, Matcher, Keys...>>
::type::type type;
};
template<typename List, int I, typename NewItem>
struct ReplaceItem {
static_assert(I > 0, "index cannot be negative");
static_assert(GetSize<List>::value > 0, "index too high");
typedef typename PrependItem<typename List::type,
typename ReplaceItem<typename List::tail, I-1,
NewItem>::type>
::type type;
};
template<typename NewItem, typename Type, typename... T>
struct ReplaceItem<TypeList<Type, T...>, 0, NewItem> {
typedef TypeList<NewItem, T...> type;
};
enum Direction {
Left = -1,
Right = 1
};
template<typename OldState, typename Input, typename NewState,
typename Output, Direction Move>
struct Rule {
typedef OldState old_state;
typedef Input input;
typedef NewState new_state;
typedef Output output;
static Direction const direction = Move;
};
template<typename A, typename B>
struct IsSame {
enum { value = false };
};
template<typename A>
struct IsSame<A, A> {
enum { value = true };
};
template<typename Input, typename State, int Position>
struct Configuration {
typedef Input input;
typedef State state;
enum { position = Position };
};
template<int A, int B>
struct Max {
enum { value = A > B ? A : B };
};
template<int n>
struct State {
enum { value = n };
static char const * name;
};
template<int n>
char const* State<n>::name = "unnamed";
struct QAccept {
enum { value = -1 };
static char const* name;
};
struct QReject {
enum { value = -2 };
static char const* name;
};
#define DEF_STATE(ID, NAME) \
typedef State<ID> NAME ; \
NAME :: name = #NAME ;
template<int n>
struct Input {
enum { value = n };
static char const * name;
template<int... I>
struct Generate {
typedef TypeList<Input<I>...> type;
};
};
template<int n>
char const* Input<n>::name = "unnamed";
typedef Input<-1> InputBlank;
#define DEF_INPUT(ID, NAME) \
typedef Input<ID> NAME ; \
NAME :: name = #NAME ;
template<typename Config, typename Transitions, typename = void>
struct Controller {
typedef Config config;
enum { position = config::position };
typedef typename Conditional<
static_cast<int>(GetSize<typename config::input>::value)
<= static_cast<int>(position),
AppendItem<InputBlank, typename config::input>,
Identity<typename config::input>>::type::type input;
typedef typename config::state state;
typedef typename GetItem<input, position>::type cell;
template<typename Item, typename State, typename Cell>
struct Matcher {
typedef typename Item::old_state checking_state;
typedef typename Item::input checking_input;
enum { value = IsSame<State, checking_state>::value &&
IsSame<Cell, checking_input>::value
};
};
typedef typename FindItem<Transitions, Matcher, state, cell>::type rule;
typedef typename ReplaceItem<input, position, typename rule::output>::type new_input;
typedef typename rule::new_state new_state;
typedef Configuration<new_input,
new_state,
Max<position + rule::direction, 0>::value> new_config;
typedef Controller<new_config, Transitions> next_step;
typedef typename next_step::end_config end_config;
typedef typename next_step::end_input end_input;
typedef typename next_step::end_state end_state;
enum { end_position = next_step::position };
};
template<typename Input, typename State, int Position, typename Transitions>
struct Controller<Configuration<Input, State, Position>, Transitions,
typename EnableIf<IsSame<State, QAccept>::value ||
IsSame<State, QReject>::value>::type> {
typedef Configuration<Input, State, Position> config;
enum { position = config::position };
typedef typename Conditional<
static_cast<int>(GetSize<typename config::input>::value)
<= static_cast<int>(position),
AppendItem<InputBlank, typename config::input>,
Identity<typename config::input>>::type::type input;
typedef typename config::state state;
typedef config end_config;
typedef input end_input;
typedef state end_state;
enum { end_position = position };
};
template<typename Input, typename Transitions, typename StartState>
struct TuringMachine {
typedef Input input;
typedef Transitions transitions;
typedef StartState start_state;
typedef Controller<Configuration<Input, StartState, 0>, Transitions> controller;
typedef typename controller::end_config end_config;
typedef typename controller::end_input end_input;
typedef typename controller::end_state end_state;
enum { end_position = controller::end_position };
};
#include <ostream>
template<>
char const* Input<-1>::name = "_";
char const* QAccept::name = "qaccept";
char const* QReject::name = "qreject";
int main() {
DEF_INPUT(1, x);
DEF_INPUT(2, x_mark);
DEF_INPUT(3, split);
DEF_STATE(0, start);
DEF_STATE(1, find_blank);
DEF_STATE(2, go_back);
/* syntax: State, Input, NewState, Output, Move */
typedef TypeList<
Rule<start, x, find_blank, x_mark, Right>,
Rule<find_blank, x, find_blank, x, Right>,
Rule<find_blank, split, find_blank, split, Right>,
Rule<find_blank, InputBlank, go_back, x, Left>,
Rule<go_back, x, go_back, x, Left>,
Rule<go_back, split, go_back, split, Left>,
Rule<go_back, x_mark, start, x, Right>,
Rule<start, split, QAccept, split, Left>> rules;
/* syntax: initial input, rules, start state */
typedef TuringMachine<TypeList<x, x, x, x, split>, rules, start> double_it;
static_assert(IsSame<double_it::end_input,
TypeList<x, x, x, x, split, x, x, x, x>>::value,
"Hmm... This is borky!");
}
Example
#include <iostream>
template <int N> struct Factorial
{
enum { val = Factorial<N-1>::val * N };
};
template<>
struct Factorial<0>
{
enum { val = 1 };
};
int main()
{
// Note this value is generated at compile time.
// Also note that most compilers have a limit on the depth of the recursion available.
std::cout << Factorial<4>::val << "\n";
}
That was a little fun but not very practical.
To answer the second part of the question:
Is this fact useful in practice?
Short Answer: Sort of.
Long Answer: Yes, but only if you are a template daemon.
To turn out good programming using template meta-programming that is really useful for others to use (ie a library) is really really tough (though do-able). To Help boost even has MPL aka (Meta Programming Library). But try debugging a compiler error in your template code and you will be in for a long hard ride.
But a good practical example of it being used for something useful:
Scott Meyers has been working extensions to the C++ language (I use the term loosely) using the templating facilities. You can read about his work here 'Enforcing Code Features'
"C++ Templates Are Turing Complete" gives an implementation of a Turing machine in templates ... which is non-trivial and proves the point in a very direct way. Of course, it also isn't very useful!
My C++ is a bit rusty, so the may not be perfect, but it's close.
template <int N> struct Factorial
{
enum { val = Factorial<N-1>::val * N };
};
template <> struct Factorial<0>
{
enum { val = 1 };
}
const int num = Factorial<10>::val; // num set to 10! at compile time.
The point is to demonstrate that the compiler is completely evaluating the recursive definition until it reaches an answer.
To give a non-trivial example: https://github.com/phresnel/metatrace , a C++ compile time ray tracer.
Note that C++0x will add a non-template, compile-time, turing-complete facility in form of constexpr:
constexpr unsigned int fac (unsigned int u) {
return (u<=1) ? (1) : (u*fac(u-1));
}
You can use constexpr-expression everywhere where you need compile time constants, but you can also call constexpr-functions with non-const parameters.
One cool thing is that this will finally enable compile time floating point math, though the standard explicitly states that compile time floating point arithmetics do not have to match runtime floating point arithmetics:
bool f(){
char array[1+int(1+0.2-0.1-0.1)]; //Must be evaluated during translation
int size=1+int(1+0.2-0.1-0.1); //May be evaluated at runtime
return sizeof(array)==size;
}
It is unspeciļ¬ed whether the value of f() will be true or false.
The factorial example actually does not show that templates are Turing complete, as much as it shows that they support Primitive Recursion. The easiest way to show that templates are turing complete is by the Church-Turing thesis, that is by implementing either a Turing machine (messy and a bit pointless) or the three rules (app, abs var) of the untyped lambda calculus. The latter is much simpler and far more interesting.
What is being discussed is an extremely useful feature when you understand that C++ templates allow pure functional programming at compile time, a formalism that is expressive, powerful and elegant but also very complicated to write if you have little experience. Also notice how many people find that just getting heavily templatized code can often require a big effort: this is exactly the case with (pure) functional languages, which make compiling harder but surprisingly yield code that does not require debugging.
The Book Modern C++ Design - Generic Programming and Design Pattern by Andrei Alexandrescu is the best place to get hands on experience with useful and powerful generic programing patterns.
I think it's called template meta-programming.
Well, here's a compile time Turing Machine implementation running a 4-state 2-symbol busy beaver
#include <iostream>
#pragma mark - Tape
constexpr int Blank = -1;
template<int... xs>
class Tape {
public:
using type = Tape<xs...>;
constexpr static int length = sizeof...(xs);
};
#pragma mark - Print
template<class T>
void print(T);
template<>
void print(Tape<>) {
std::cout << std::endl;
}
template<int x, int... xs>
void print(Tape<x, xs...>) {
if (x == Blank) {
std::cout << "_ ";
} else {
std::cout << x << " ";
}
print(Tape<xs...>());
}
#pragma mark - Concatenate
template<class, class>
class Concatenate;
template<int... xs, int... ys>
class Concatenate<Tape<xs...>, Tape<ys...>> {
public:
using type = Tape<xs..., ys...>;
};
#pragma mark - Invert
template<class>
class Invert;
template<>
class Invert<Tape<>> {
public:
using type = Tape<>;
};
template<int x, int... xs>
class Invert<Tape<x, xs...>> {
public:
using type = typename Concatenate<
typename Invert<Tape<xs...>>::type,
Tape<x>
>::type;
};
#pragma mark - Read
template<int, class>
class Read;
template<int n, int x, int... xs>
class Read<n, Tape<x, xs...>> {
public:
using type = typename std::conditional<
(n == 0),
std::integral_constant<int, x>,
Read<n - 1, Tape<xs...>>
>::type::type;
};
#pragma mark - N first and N last
template<int, class>
class NLast;
template<int n, int x, int... xs>
class NLast<n, Tape<x, xs...>> {
public:
using type = typename std::conditional<
(n == sizeof...(xs)),
Tape<xs...>,
NLast<n, Tape<xs...>>
>::type::type;
};
template<int, class>
class NFirst;
template<int n, int... xs>
class NFirst<n, Tape<xs...>> {
public:
using type = typename Invert<
typename NLast<
n, typename Invert<Tape<xs...>>::type
>::type
>::type;
};
#pragma mark - Write
template<int, int, class>
class Write;
template<int pos, int x, int... xs>
class Write<pos, x, Tape<xs...>> {
public:
using type = typename Concatenate<
typename Concatenate<
typename NFirst<pos, Tape<xs...>>::type,
Tape<x>
>::type,
typename NLast<(sizeof...(xs) - pos - 1), Tape<xs...>>::type
>::type;
};
#pragma mark - Move
template<int, class>
class Hold;
template<int pos, int... xs>
class Hold<pos, Tape<xs...>> {
public:
constexpr static int position = pos;
using tape = Tape<xs...>;
};
template<int, class>
class Left;
template<int pos, int... xs>
class Left<pos, Tape<xs...>> {
public:
constexpr static int position = typename std::conditional<
(pos > 0),
std::integral_constant<int, pos - 1>,
std::integral_constant<int, 0>
>::type();
using tape = typename std::conditional<
(pos > 0),
Tape<xs...>,
Tape<Blank, xs...>
>::type;
};
template<int, class>
class Right;
template<int pos, int... xs>
class Right<pos, Tape<xs...>> {
public:
constexpr static int position = pos + 1;
using tape = typename std::conditional<
(pos < sizeof...(xs) - 1),
Tape<xs...>,
Tape<xs..., Blank>
>::type;
};
#pragma mark - States
template <int>
class Stop {
public:
constexpr static int write = -1;
template<int pos, class tape> using move = Hold<pos, tape>;
template<int x> using next = Stop<x>;
};
#define ADD_STATE(_state_) \
template<int> \
class _state_ { };
#define ADD_RULE(_state_, _read_, _write_, _move_, _next_) \
template<> \
class _state_<_read_> { \
public: \
constexpr static int write = _write_; \
template<int pos, class tape> using move = _move_<pos, tape>; \
template<int x> using next = _next_<x>; \
};
#pragma mark - Machine
template<template<int> class, int, class>
class Machine;
template<template<int> class State, int pos, int... xs>
class Machine<State, pos, Tape<xs...>> {
constexpr static int symbol = typename Read<pos, Tape<xs...>>::type();
using state = State<symbol>;
template<int x>
using nextState = typename State<symbol>::template next<x>;
using modifiedTape = typename Write<pos, state::write, Tape<xs...>>::type;
using move = typename state::template move<pos, modifiedTape>;
constexpr static int nextPos = move::position;
using nextTape = typename move::tape;
public:
using step = Machine<nextState, nextPos, nextTape>;
};
#pragma mark - Run
template<class>
class Run;
template<template<int> class State, int pos, int... xs>
class Run<Machine<State, pos, Tape<xs...>>> {
using step = typename Machine<State, pos, Tape<xs...>>::step;
public:
using type = typename std::conditional<
std::is_same<State<0>, Stop<0>>::value,
Tape<xs...>,
Run<step>
>::type::type;
};
ADD_STATE(A);
ADD_STATE(B);
ADD_STATE(C);
ADD_STATE(D);
ADD_RULE(A, Blank, 1, Right, B);
ADD_RULE(A, 1, 1, Left, B);
ADD_RULE(B, Blank, 1, Left, A);
ADD_RULE(B, 1, Blank, Left, C);
ADD_RULE(C, Blank, 1, Right, Stop);
ADD_RULE(C, 1, 1, Left, D);
ADD_RULE(D, Blank, 1, Right, D);
ADD_RULE(D, 1, Blank, Right, A);
using tape = Tape<Blank>;
using machine = Machine<A, 0, tape>;
using result = Run<machine>::type;
int main() {
print(result());
return 0;
}
Ideone proof run: https://ideone.com/MvBU3Z
Explanation: http://victorkomarov.blogspot.ru/2016/03/compile-time-turing-machine.html
Github with more examples: https://github.com/fnz/CTTM
You can check this article from Dr. Dobbs on a FFT implementation with templates which I think not that trivial.
The main point is to allow the compiler to perform a better optimization than for non template implementations as the FFT algorithm uses a lot of constants ( sin tables for instance )
part I
part II
It's also fun to point out that it is a purely functional language albeit nearly impossible to debug. If you look at James post you will see what I mean by it being functional. In general it's not the most useful feature of C++. It wasn't designed to do this. It's something that was discovered.
It may be useful if you want to compute constants at compile time, at least in theory. Check out template metaprogramming.
An example which is reasonably useful is a ratio class. There are a few variants floating around. Catching the D==0 case is fairly simple with partial overloads. The real computing is in calculating the GCD of N and D and compile time. This is essential when you're using these ratios in compile-time calculations.
Example: When you're calculating centimeters(5)*kilometers(5), at compile time you'll be multiplying ratio<1,100> and ratio<1000,1>. To prevent overflow, you want a ratio<10,1> instead of a ratio<1000,100>.
A Turing machine is Turing-complete, but that doesn't mean you should want to use one for production code.
Trying to do anything non-trivial with templates is in my experience messy, ugly and pointless. You have no way to "debug" your "code", compile-time error messages will be cryptic and usually in the most unlikely places, and you can achieve the same performance benefits in different ways. (Hint: 4! = 24). Worse, your code is incomprehensible to the average C++ programmer, and will be likely be non-portable due to wide ranging levels of support within current compilers.
Templates are great for generic code generation (container classes, class wrappers, mix-ins), but no - in my opinion the Turing Completeness of templates is NOT USEFUL in practice.
Just another example of how not to program :
template<int Depth, int A, typename B>
struct K17 {
static const int x =
K17 <Depth+1, 0, K17<Depth,A,B> >::x
+ K17 <Depth+1, 1, K17<Depth,A,B> >::x
+ K17 <Depth+1, 2, K17<Depth,A,B> >::x
+ K17 <Depth+1, 3, K17<Depth,A,B> >::x
+ K17 <Depth+1, 4, K17<Depth,A,B> >::x;
};
template <int A, typename B>
struct K17 <16,A,B> { static const int x = 1; };
static const int z = K17 <0,0,int>::x;
void main(void) { }
Post at C++ templates are turing complete