WEBVTT

00:00.120 --> 00:00.690
Hello again!

00:00.930 --> 00:08.010
In this video, we are going to look at extern templates. Wxtern templates are a useful way of addressing

00:08.130 --> 00:10.040
the problem of "template bloat".

00:10.470 --> 00:12.390
So let's find out what that involves.

00:13.980 --> 00:19.770
When we have a template, we usually put the full template definition in a header.

00:20.550 --> 00:26.170
If we have a template function, we would put the function body. And if we have a template class, then

00:26.180 --> 00:28.350
we would put the full class definition.

00:29.400 --> 00:33.060
This is different from the way that we normally handle functions and classes.

00:33.420 --> 00:39.720
And the reason is, when we use the template in a source code file, we want the compiler to be able

00:39.720 --> 00:42.570
to instantiate the template for us, when it is used.

00:43.080 --> 00:47.580
And to do this, the compiler has to be able to see the full template definition.

00:49.780 --> 00:53.320
If we have the same instantiation in lots of files.

00:53.320 --> 00:56.740
So we have a file where we call the function with a string argument.

00:57.250 --> 01:00.880
So the compiler will instantiate this with string as the parameter.

01:01.540 --> 01:04.030
And then we have lots and lots of other files where this happens.

01:04.570 --> 01:09.190
The compiler will actually instantiate the template separately, in every single file.

01:09.670 --> 01:15.490
So we end up with hundreds of source code files, which contain the same instantiation code.

01:16.510 --> 01:19.870
And you might think that is rather stupid. Why does the compiler not know that

01:19.870 --> 01:21.760
it has already done that instantiation?

01:22.960 --> 01:28.660
In fact, people have tried to write compilers which do keep track of this. And they only instantiate

01:28.660 --> 01:29.530
when necessary.

01:29.950 --> 01:35.800
But it is actually very, very hard to get it right. Dealing with all the unusual cases. And also getting

01:35.800 --> 01:41.230
the correct code into the executable, and making sure it works with debuggers and other tools.

01:41.710 --> 01:43.330
And in fact, it is just too difficult.

01:43.540 --> 01:48.580
So compiler writers have tried this. But they have always given up and gone back to the crude but effective

01:48.580 --> 01:51.220
way, of always instantiating regardless.

01:54.470 --> 01:59.840
So we have 269 source code files which include the template instantiation code.

02:00.500 --> 02:07.610
When we convert those to binary, we will have 269 object files which include the binary code for

02:07.610 --> 02:09.230
the string instantiation.

02:09.980 --> 02:14.780
That's not actually going to be a problem for creating the program binary, because the linker will

02:15.080 --> 02:16.250
remove the duplicates.

02:16.580 --> 02:18.800
It will just pick one of these and ignore all the rest.

02:19.220 --> 02:24.500
So we only have one implementation code in the actual binary program executable.

02:25.430 --> 02:30.230
However, this does increase the size of the object file because we have all this unnecessary code.

02:30.740 --> 02:36.620
It also increases compilation time because the compiler is doing all this unnecessary work. And also

02:36.620 --> 02:41.180
these unnecessarily large object files take longer to process, and they use more memory.

02:42.320 --> 02:45.590
And this can be a serious problem in large projects, which take a long time to build.

02:47.330 --> 02:51.320
The traditional way of doing this, is to manually instantiate the templates.

02:53.530 --> 02:59.080
So in the header we only put the template declaration without the actual body of the function, or the

02:59.260 --> 03:00.250
class definition.

03:01.540 --> 03:04.780
We are also going to use another function which is not a template function.

03:05.320 --> 03:08.410
I've just put the definition in here, for convenience.

03:10.230 --> 03:14.430
Then we have our source code file which defines this function.

03:14.940 --> 03:17.160
This will actually call the template function.

03:17.790 --> 03:21.960
It includes the header, which just has the declaration of the template function.

03:22.320 --> 03:27.300
So at this point, the compiler cannot see the definition, which means the compiler cannot instantiate

03:27.300 --> 03:29.490
the function in this source code.

03:31.070 --> 03:33.230
So we do not have any instantiation here.

03:33.860 --> 03:39.890
And then in one, and only one source code, we provide the template definition.

03:41.580 --> 03:46.200
So that means that in this source code file, the compiler will be able to instantiate the template,

03:46.740 --> 03:49.260
and then we provide a manual instantiation.

03:49.650 --> 03:52.770
So this is the instantiation which takes a string argument.

03:53.610 --> 03:59.250
We need to put the template keyword in front, so the compiler knows that it is a template instantiation

03:59.250 --> 04:01.950
and not just some non-template function.

04:03.980 --> 04:08.420
And then when we call the function in here, this will use the instantiation.

04:08.990 --> 04:14.330
And when we call the function which calls the template, then that will call that this instantiation

04:14.330 --> 04:14.570
here.

04:14.900 --> 04:18.770
So we only have one instantiation of the template in the entire program.

04:20.850 --> 04:21.270
There we are.

04:21.930 --> 04:22.920
So that is possible.

04:22.920 --> 04:25.020
But it does mean a lot of work.

04:26.040 --> 04:31.650
The programmers need to keep track of which templates they need to instantiate and where to instantiate them.

04:32.100 --> 04:34.710
And in large projects, that can become a bit of a headache.

04:35.610 --> 04:38.010
So it is not really all that useful.

04:38.280 --> 04:41.520
Although, before C++11, this was the only way to do it.

04:44.700 --> 04:48.990
Before we get on to extern templates, let's look at extern variables.

04:49.860 --> 04:52.050
These were inherited from C.

04:52.860 --> 04:58.650
They were used to make global variables accessible across different source files, which is a very bad idea.

04:58.980 --> 05:01.080
This is why object oriented programming was invented,

05:01.080 --> 05:05.040
in fact, because global variables were causing so many problems.

05:06.930 --> 05:11.130
So we can declare a variable as extern, in a source file or in a header.

05:11.700 --> 05:15.180
And this means that the actual variable is defined somewhere else.

05:15.690 --> 05:17.850
So this is just a declaration of the variable.

05:19.140 --> 05:21.280
And then we can use this in different source code files.

05:22.710 --> 05:25.890
And then there has to be exactly one file where the variable is actually defined.

05:26.460 --> 05:30.660
So we have the variable without extern. And also, this must not be initialized.

05:31.350 --> 05:36.540
So this will actually define the meaning of life variable and the ones in the other source code are,

05:36.810 --> 05:43.950
if you like, references to this one. Not the references that we have in C++, but a sort of

05:43.950 --> 05:44.850
"link-time" reference.

05:47.450 --> 05:49.010
So here is our header.

05:49.010 --> 05:51.860
We have the meaning_of_life, which is extern.

05:52.190 --> 05:54.590
So this means it is defined somewhere else.

05:55.580 --> 05:56.640
Then we have our function

05:57.290 --> 05:58.070
prototype again.

06:00.110 --> 06:03.740
We have our function which uses this meaning_of_life variable.

06:04.250 --> 06:05.750
It includes the header.

06:05.960 --> 06:07.610
So this will be an extern variable.

06:07.610 --> 06:10.610
So this function will know that the variable is defined somewhere else.

06:11.630 --> 06:17.660
And then with our main function. Again, this will use the variable which is defined somewhere else.

06:18.890 --> 06:20.790
And finally, we have the "somewhere else".

06:20.810 --> 06:24.620
So here it is. This is where the meaning_of_life is actually defined.

06:25.400 --> 06:27.140
And we are not allowed to initialize it here.

06:29.210 --> 06:35.390
So the function will initialize it, or set its value to 42, and then the main() function will print

06:35.390 --> 06:35.780
it out.

06:37.100 --> 06:37.880
And this all works.

06:41.110 --> 06:42.140
In C++11,

06:42.200 --> 06:45.130
we can also use the extern keyword with templates.

06:46.420 --> 06:52.000
We write out the template definition as usual, with the function body or the class definition.

06:52.510 --> 06:57.100
And then, after that, we have an instantiation which is declared as extern.

06:57.580 --> 07:02.950
So this will tell the compiler that there is an instantiation, somewhere else, in the source code for

07:02.950 --> 07:03.490
the program.

07:05.170 --> 07:09.940
So then when we have our files which call the function with a string argument, the compiler will

07:09.940 --> 07:15.040
not instantiate the function in these files, because it knows that the instantiation is somewhere else.

07:16.240 --> 07:22.450
And then we have one file, where the function is explicitly instantiated, without the extern keyword.

07:22.990 --> 07:25.870
And then the compiler will that generates the instantiation that.

07:26.200 --> 07:28.960
So we only have one instantiation in the entire program.

07:30.310 --> 07:31.390
So here is our code.

07:31.390 --> 07:34.690
We have our template function with the function body.

07:35.470 --> 07:39.670
Then we have an instantiation which is declared as extern.

07:40.810 --> 07:46.810
So any source files which include this header will know that the instantiation is done somewhere else.

07:49.100 --> 07:53.780
Then we have our function which calls this instantiation, which is done somewhere else.

07:54.890 --> 07:58.730
We have our main() function which calls the instantiation, which is done somewhere else.

07:59.960 --> 08:05.000
And then we have the implementation file, where the instantiation is done explicitly.

08:05.300 --> 08:10.880
So this will be where the template is instantiated, and this will contain the only copy of the source

08:10.880 --> 08:12.770
code for the template body.

08:16.230 --> 08:17.160
And that compiles.

08:18.000 --> 08:21.540
So just to show the difference this makes, I had a little experiment.

08:21.960 --> 08:25.050
So these are the sizes of the object files without extern.

08:25.770 --> 08:28.950
And this is exactly the same code, but using extern templates.

08:29.490 --> 08:31.710
So the object files are about 25% smaller.

08:32.790 --> 08:36.960
And also the time taken would have been a lot shorter, because, in these ones, the compiler would have

08:36.960 --> 08:40.770
to generate the instantiation in every source file.

08:41.340 --> 08:43.470
And in these ones, it would only have to do it once.

08:43.950 --> 08:47.190
And if you have hundreds of these files, then that would obviously save a lot of time.

08:49.820 --> 08:52.760
And finally, this also works the same way with class templates.

08:53.030 --> 08:56.060
So we puts the full template class definition in the header.

08:56.630 --> 08:59.000
Then we have an extern instantiation.

08:59.720 --> 09:03.560
So Test with int parameter will be instantiated somewhere else.

09:04.850 --> 09:10.010
Then we have all these files which use the extern instantiation, and then we have an implementation

09:10.010 --> 09:12.800
file, where the instantiation takes place.

09:13.550 --> 09:15.110
Okay, so that is it for this video.

09:15.500 --> 09:16.320
I will see you next time.

09:16.320 --> 09:18.500
But until then, keep coding!
