Home | Projects | Notes > C++ Programming > Generic Programming & Templates
Generic programming / Meta-programming
Preprocessor macros
Function templates
Class templates
Generic programming
"Writing code that works with a variety of types as arguments, as long as those argument types meet specific syntactic and semantic requirements." - Bjarne Stroustrup
Macros - BEWARE!
Function templates
Class templates
#define
)C++ preprocessor directives
No type information
Simple substitution
Example of macros
Source file (.cpp)
xxxxxxxxxx
71
2
3
4if (num > MAX_SIZE)
5 std::cout << "Too big";
6
7double area = PI * r * r;
Expanded source file (.i)
xxxxxxxxxx
71// Removed
2// Removed
3
4if (num > 100)
5 std::cout << "Too big";
6
7double area = 3.14159 * r * r;
All macros will be expanded by the preprocessor. (Textual substitution)
Making code more generic by using macro with arguments instead of multiple functions
xxxxxxxxxx
51
2
3std::cout << MAX(10, 20) << std::endl; // 20
4std::cout << MAX(2.4, 3.5) << std::endl; // 3.5
5std::cout << MAX('A', 'C') << std::endl; // C
Instead of
xxxxxxxxxx
31int max(int a, int b) { return (a > b) ? a : b; }
2double max(double a, double b) { return (a > b) ? a : b; }
3char max(char a, char b) { return (a > b) ? a : b; }
Caution! Use parenthesis generously!
xxxxxxxxxx
71
2
3result = SQUARE(5); // Expect 25
4result = 5 * 5; // Get 25
5
6result = 100 / SQUARE(5); // Expect 4
7result = 100 / 5 * 5; // Get 100
Instead, guard each argument with parenthesis
xxxxxxxxxx
71
2
3result = SQUARE(5); // Expect 25
4result = ((5) * (5)); // Still get 25
5
6result = 100 / SQUARE(5); // Expect 4
7result = 100 / ((5) * (5)); // Now we get 4
What is a C++ Template?
Blueprint
Function and class templates
Allow plugging-in any data type
Compiler generates the appropriate function/class from the blue print
Generic programming / meta-programming
Revisiting the previous example, we can replace the type we want to generalize with a name, say T
. But, now this won't compile.
xxxxxxxxxx
11T max(T a, T b) { return (a > b) ? a : b; }
We need to tell the compiler that this is a template function, and that T
is the template parameter.
xxxxxxxxxx
21template <typename T>
2T max(T a, T b) { return (a > b) ? a : b; }
Can use class
instead of typename
xxxxxxxxxx
21template <class T>
2T max(T a, T b) { return (a > b) ? a : b; }
Now the compiler can generate the appropriate function from the template.
Note, this happens at compile-time.
xxxxxxxxxx
41int a{10};
2int b{20};
3
4std::cout << max<int>(a, b);
Many times the compiler can deduce the type and the template parameter is not needed. Depending on the type of a
and b
, the compiler will figure it out.
xxxxxxxxxx
21std::cout << max<double>(c, d);
2std::cout << max(c, d);
And we can use almost any type we need.
xxxxxxxxxx
41char a{'A'};
2char b{'B'};
3
4std::cout << max(a, b) << std::endl;
Notice that the type MUST support the >
operator either natively or as an overloaded operator (operator>
).
xxxxxxxxxx
21template <typename T>
2T max(T a, T b) { return (a > b) ? a : b; }
The following will not compile unless Player
overloads operator>
.
xxxxxxxxxx
41Player p1{"Hero", 100, 20};
2Player p2{"Enemy", 99, 3};
3
4std::cout << max<Player>(p1, p2);
Templates can have multiple parameters, and their types can be different.
xxxxxxxxxx
21template <typename T1, typename T2>
2void func(T1 a, T2 b) { std::cout << a << " " << b; }
When we use the function we provide the template parameters. often the compiler can deduce them.
xxxxxxxxxx
21func<int, double>(10, 5.4);
2func(10, 5.4);