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?
|
|
|
Example
That was a little fun but not very practical. To answer the second part of the question: 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' |
|||||||||||||||||||||
|
|
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.
|
|||||||||||||||||||||
|
|
"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.
The point is to demonstrate that the compiler is completely evaluating the recursive definition until it reaches an answer. |
|||||
|
|
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. |
|||
|
|
|
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. |
|||||
|
|
To give a non-trivial example: http://gitorious.org/metatrace , a C++ compile time ray tracer. Note that C++0x will add a non-template, compile-time, turing-complete facility in form of
You can use 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:
|
||||
|
|
|
I think it's called template meta-programming. |
|||||||||||||
|
|
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 ) |
|||
|
|
|
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. |
||||
|
|
|
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. |
|||||
|
|
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>. |
|||
|
|
|
Well, here's a compile time Turing Machine implementation running a 4-state 2-symbol busy beaver
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 |
|||
|
|
|
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) { }
|
|||||||||
|