WEBVTT

00:00.120 --> 00:04.680
Hello again! In this video, we are going to start our section on compile time programming.

00:07.250 --> 00:13.640
Compile time programming means we are writing a code which will be executed by the compiler, during the

00:13.640 --> 00:16.820
compilation, and not by the program at run time.

00:17.870 --> 00:22.970
So none of this code will actually appear in the program. Just the result of the computation.

00:23.930 --> 00:29.420
So this means that, when the program runs, it instantly has access to the result, and does not need

00:29.420 --> 00:31.490
to perform any calculation itself.

00:32.330 --> 00:37.700
So that means the operation takes zero time. And you cannot really get any faster than that!

00:38.450 --> 00:43.820
So obviously being able to do this is very desirable, but there are practical limitations, on what you

00:43.820 --> 00:46.010
can do, and how easily you can do it.

00:49.060 --> 00:53.620
This was actually possible in C, using something called "preprocessor macros".

00:54.790 --> 00:58.480
So the processor is the very first stage of compilation.

00:59.080 --> 01:04.060
This is when the compiler goes through and removes comments, compresses whitespace and so on.

01:04.870 --> 01:09.550
You can give directives to the preprocessor, which start with a hash symbol.

01:10.300 --> 01:17.050
And one of the directives you can give is to define a symbol. So you can put hash define some symbol.

01:17.530 --> 01:22.750
And the prerocessor will replace that symbol, by whatever you define it as.

01:23.530 --> 01:26.980
And you can provide arguments, and then the arguments will be substituted.

01:30.130 --> 01:32.650
So here we have our macro function.

01:33.190 --> 01:36.970
It just takes two arguments, and returns whichever one is the greater.

01:38.140 --> 01:46.360
If we put something like max(a, b) in our code, this will substitute x with a and y with b, and then

01:46.360 --> 01:50.290
paste the result into the compiler's view of the source code.

01:50.770 --> 01:55.060
So the next stage of compilation will actually see this, instead of the max().

01:58.200 --> 01:59.730
There are a few problems with this.

02:00.150 --> 02:05.430
The first one is, there is no type information at all, because the compiler has not actually got that far

02:05.430 --> 02:05.700
yet.

02:05.730 --> 02:09.750
This is just text processing. So there is no type safety here at all.

02:10.620 --> 02:16.170
The second one is, we need to put some brackets around the arguments when we use them, because of the

02:16.170 --> 02:17.640
operator precedence rules.

02:18.210 --> 02:21.570
We may get things which behave unexpectedly, or just do not compile.

02:22.920 --> 02:28.680
And the third one is, if any of these arguments have side effects, then they may be evaluated more

02:28.680 --> 02:29.190
than once.

02:30.850 --> 02:38.260
For example, if we increment the argument, then this will be replaced by code which looks like this.

02:40.000 --> 02:44.230
So if incrementing "a" is bigger than "b", then "a" gets incremented again.

02:44.590 --> 02:46.660
So in that case, "a" is incremented twice.

02:47.470 --> 02:51.670
On the other hand, if incrementing "a" is less than "b", then it only gets incremented once.

02:52.270 --> 02:53.770
So you have inconsistent behavior.

02:54.130 --> 02:56.650
And also, it is not obvious from looking at this code,

02:56.980 --> 02:59.080
that "a" might be incremented twice.

03:01.800 --> 03:04.440
So if you do fairly standard things, there is no problem.

03:04.830 --> 03:10.260
But when we do the increment, it gets incremented twice, in the case where ++a is greater than

03:10.260 --> 03:10.530
"b".

03:14.460 --> 03:17.100
C++ has had templates for quite a long time.

03:17.610 --> 03:21.150
These were originally introduced to support generic programming.

03:21.510 --> 03:24.920
So, you all know about things like the vector class in the standard library.

03:25.500 --> 03:28.200
The actual code for handling vectors is all the same.

03:28.650 --> 03:31.230
The only thing which differs is the type of the element.

03:31.830 --> 03:38.940
So you can write code which works with a generic element type, and then provide the type. And the compiler

03:39.010 --> 03:41.430
will generate the correct code for that type.

03:43.160 --> 03:50.540
Later on, some clever person discovered that templates actually implement a Turing-complete programming language.

03:51.200 --> 03:56.450
So that means that, in theory, you can solve any problem using templates. If you are masochistic enough!

03:58.360 --> 04:02.410
You can use the template parameters for representing state variables.

04:03.520 --> 04:06.940
You can use recursive instantiation to simulate loops.

04:07.780 --> 04:09.280
(Sorry, I have a bit of a headache coming on)

04:11.110 --> 04:11.350
(Yeah,

04:11.380 --> 04:11.710
okay.)

04:12.040 --> 04:16.240
You can use template specialization for implementing controls.

04:16.330 --> 04:16.720
(Oh)

04:16.880 --> 04:17.150
(Oh)

04:17.560 --> 04:17.680
(Oh)

04:18.620 --> 04:18.900
(It is no good)

04:19.000 --> 04:19.350
(I am sorry)

04:21.220 --> 04:25.870
And you can also use integer operations to calculate results, which is actually not very painful.

04:25.870 --> 04:26.980
(But I am sorry)

04:26.980 --> 04:27.850
(I have had enough already. <Laughs>)

04:31.540 --> 04:31.990
Template

04:31.990 --> 04:34.390
metaprogramming is mainly done by library developers.

04:34.390 --> 04:38.790
So if you mainly write applications, you do not need to worry too much about it.

04:40.670 --> 04:42.860
It can be used for compile-time programming.

04:43.760 --> 04:49.730
It can even be used for the original intended purpose, which is writing generic code, to avoid duplication.

04:50.360 --> 04:52.550
The Standard Template Library is a good example.

04:54.330 --> 04:58.260
You can use template metaprogramming for making decisions at compile time.

04:58.800 --> 05:04.680
For example, if your hardware has some special features, you can take advantage of those, to make your

05:04.680 --> 05:05.630
codes more efficient.

05:05.640 --> 05:09.480
Or you can write code to make it easier to port to different computers.

05:10.620 --> 05:15.900
You can use it for domain-specific programming so you can write a kind of language on top of C++,

05:16.230 --> 05:19.350
which is customized for the problem that you are trying to solve.

05:20.220 --> 05:24.150
And you can use it for expressing complex software patterns and concepts.

05:27.920 --> 05:32.210
Templates metaprogramming mostly uses class templates, rather than functions.

05:33.470 --> 05:39.170
There is a convention: if you have a member which represents a numeric result, or some kind of value, that is

05:39.170 --> 05:40.370
called "value".

05:41.270 --> 05:44.840
And if the member represents a type, then it is called "type".

05:46.640 --> 05:47.110
Template

05:47.120 --> 05:54.080
metaprogramming makes heavy use of template specialization. C++11 introduced something called "type

05:54.080 --> 05:54.500
traits".

05:54.860 --> 05:58.340
You can use these to get information about the properties of types.

05:59.210 --> 06:05.060
For example, if you want to know if your templates parameter is an arithmetic type, you can do something

06:05.060 --> 06:05.630
like that.

06:07.640 --> 06:08.510
Let's have a quick demo!

06:09.080 --> 06:10.760
So, is_arithmetic.

06:11.780 --> 06:13.320
This is actually a template

06:13.340 --> 06:17.060
claas. The class has a member called value, which is a bool.

06:17.150 --> 06:21.740
And this will be true if int is an arithmetic type, and it will be false if not.

06:22.610 --> 06:24.050
So we expect this to be true.

06:25.400 --> 06:29.570
is_floating_point, with parameter int. We expect that to be false.

06:31.200 --> 06:33.300
is_class. with parameter A?

06:33.840 --> 06:35.370
Here is our class A.

06:35.490 --> 06:37.200
So we expect that to be true.

06:38.280 --> 06:43.470
And then, is_pointer, with a C type string as parameter.

06:43.620 --> 06:45.090
So we expect that to be true as well.

06:45.630 --> 06:47.150
(Do not know why this is says false. Sorry about that)

06:54.660 --> 06:54.990
Okay.

06:54.990 --> 06:56.970
So those are the results that we would expect.

06:57.540 --> 06:59.580
So maybe nothing too surprising.

07:00.480 --> 07:04.180
There are lots and lots of these. And most of them are pretty obscure, actually.

07:04.530 --> 07:06.660
So that just gives you an idea of what is possible.

07:09.130 --> 07:13.930
So with compile-time programming, until C++ 11, your only real option was templates.

07:14.590 --> 07:17.230
The syntax for these is clumsy and verbose.

07:18.160 --> 07:24.160
Doing logical operations is very complicated and hard to understand. The error messages are extremely

07:24.160 --> 07:29.320
hard to understand as well. And you cannot just go into the debugger and see what is going on.

07:29.710 --> 07:34.690
A debugger requires a running program in memory, and these are actually inside the compiler.

07:36.730 --> 07:43.150
With C++11, we have constexpr. So this allows you to write things that look like normal C++ code,

07:43.540 --> 07:47.980
but are executed by the compiler, at compile time, rather than runtime.

07:48.940 --> 07:54.580
And the error messages look like the errors you get with normal C++ code, and not the horrible ones you

07:54.580 --> 07:55.420
get with templates.

07:56.560 --> 07:56.860
Okay.

07:56.860 --> 07:58.000
So that is it for this video.

07:58.510 --> 07:59.320
I will see you next time.

07:59.320 --> 08:01.630
But until then, keep coding!
