I'm trying to understand what metaprogramming is general and what it is in C++ in particular. If I search for c++ metaprogramming I do get tutorials of template metaprogramming (TMP), but no explanation of if it only categorizes a specific use of templates or all usages of templates.
My question is if all usages of templates in C++ is categorized as metaprogramming. An explanation of why it is or isn't would also be helpful. Thank you.
Template metaprogramming (TMP) in C++ is a technique for expressing and executing arbitrary algorithms in compile-time using C++ templates. It is usually enabled by the use of template specialization to emulate conditional branches and recursive template definition to emulate loops. The most well-known example is a compile-time factorial computation:
template <unsigned int n>
struct factorial {
// recursive definition to emulate a loop or a regular recursion
enum { value = n * factorial<n - 1>::value };
};
// specialization that describes "break" condition for the recursion
template <>
struct factorial<0> {
enum { value = 1 };
};
which uses both of the aforementioned techniques.
A far more common, however, is a use of TMP for type detection and transformation rather than actual numeric computation, e.g. a standard std::is_pointer
utility (source):
// generic definition that emulates "false" conditional branch
template<class T>
struct is_pointer_helper : std::false_type {};
// a specialization that emulates "true" conditional branch
template<class T>
struct is_pointer_helper<T*> : std::true_type {};
template<class T>
struct is_pointer : is_pointer_helper< typename std::remove_cv<T>::type > {};
Most of the utilities provided by the standard type_traits header are implemented using TMP techniques.
Given that TMP algorithms are expressed using type definitions, it's worth mentioning that TMP is a form of declarative programming in which the logic of computation is expressed without the use of explicit control flow statements (if
, else
, for
, etc...).
The short answer is: No. If the templates aren't used for expressing a compile-time algorithm then it's not a metaprogramming, it's a generic programming.
The primary goal for introducing templates in C++ was to enable generic programming, that is to allow reusing the same algorithms (find
, copy
, sort
, etc...) and data structures (vector
, list
, map
, etc...) for any types, including user-defined ones, that satisfy certain requirements.
In fact TMP in C++ was discovered by accident and was not the intended use of templates.
In summary: Template metaprogramming in C++ is the use of templates to express a compile-time algorithm, most (all?) other uses of C++ templates is a form of generic programming.
My question is if all usages of templates in C++ is categorized as metaprogramming.
No.
Not all usages of templates, in C++, are metaprogramming.
Obviously it's a question of definitions but, in C++, "metaprogramming" is synonymous of "compile-time computation".
So with templates we do metaprogramming (specifically template metaprogramming) but not all uses of templates are metaprogramming.
A simple counter-example
template <typename K, typename V>
void printKeyVal (K const & k, V const & v)
{ std::cout << k << ": " << v << std::endl; }
The preceding printKeyVal()
is a template function that print, to standard output (so run-time, not compile-time), a couple of generic values.
It can't run compile-time so it's "template" but isn't "metaprogramming".
More in general: std::vector
is a template class that uses memory allocation. And memory allocation (till C++17; maybe in future can be different) can't be used in compile-time code.
So std::vector
(contrary to std::array
that, having a fixed size, doesn't use memory allocation) is a template feature that can't be used (when the use involve the instantiation of a std::vector
object) for metaprogramming.
I'm trying to understand what metaprogramming is general and what it is in C++ in particular
You haven't said what you understand by metaprogramming in general yet, so your answers don't have a common starting point.
I'm going to assume the wikipedia definition is good enough for this:
Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data.
... can be used to move computations from run-time to compile-time, to generate code using compile time computations ...
C++ doesn't generally allow self-modifying code, so I'm ignoring that. I'm also choosing not to count the preprocessor, as textual substitution at (or arguably just before) compile time is not the same as operating on the semantics of the program.
My question is if all usages of templates in C++ is categorized as metaprogramming
No, it is not.
Consider, for reference:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
which is loosely the way to write a generic (type-agnostic) max
function without using templates. I've already said I don't count the preprocessor as metaprogramming, but in any case it always produces identical code whenever it is used.
It simply delegates parsing that code, and worrying about types and whether a>b
is defined to the compiler, in a later translation phase. Nothing here operates at compile time to produce different resulting code depending on ... anything. Nothing, at compile time, is computed.
Now, we can compare the template version:
template <typename T>
T max(T a, T b) { return a > b ? a : b; }
This does not simply perform a textual substitution. The process of instantiation is more complex, name lookup rules and overloads may be considered, and in some sense different instantiations may not be textually equivalent (eg. one may use bool ::operator< (T,T)
and one bool T::operator<(T const&)
or whatever).
However, the meaning of each instantiation is the same (assuming compatible definitions of operator<
for different types, etc.) and nothing was computed at compile time apart from the compiler's usual (mechanical) process of resolving types and names and so on.
As an aside, it's definitely not enough that your program contains instructions for the compiler to tell it what to do, because that's what all programming is.
Now, there are marginal cases like
template <unsigned N>
struct factorial() { enum { value = N * factorial<N-1>::value }; };
which do move a computation to compile time (and in this case a non-terminating one since I can't be bothered to write the terminal case), but are arguably not metaprogramming.
Even though the Wikipedia definition mentioned moving computations to compile time, this is only a value computation - it's not making a compile-time decision about the structure or semantics of your code.
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