WEBVTT

00:00.210 --> 00:05.610
Hello again! In this video, we are going to look at the entity manager and object creation.

00:06.180 --> 00:11.640
In fact, there is quite a lot to unpack with the entity manager, so it is going to be split over two

00:11.640 --> 00:12.360
videos.

00:12.360 --> 00:18.390
This one will deal with creating objects, and the second one will deal with operating on objects.

00:21.640 --> 00:28.090
So, just to remind ourselves, the entity manager has a member function called create(), which will create

00:28.090 --> 00:30.490
a new entity object in the game.

00:30.910 --> 00:37.180
It will add it to all_entities, which is a vector of unique_ptrs to each entity in the game.

00:37.570 --> 00:42.430
And it will also add an alias to the object to this grouped_entity,

00:42.430 --> 00:43.240
which groups

00:43.240 --> 00:45.010
all the entities by their type.

00:46.500 --> 00:52.200
The arguments to this create() call will be used for constructing the object, so they are going to be passed

00:52.200 --> 00:53.970
to the entity constructor.

00:54.900 --> 00:59.970
And it may be that different entities will require different numbers of arguments, or different types

00:59.970 --> 01:00.810
of arguments.

01:00.960 --> 01:07.170
So we may have to write overloads. And also, some of the arguments may be expensive to copy, or they could

01:07.170 --> 01:08.610
be move-only objects.

01:08.850 --> 01:12.570
So we may have to write overloads for rvalue references as well.

01:13.290 --> 01:17.760
So instead of writing all that code ourselves, why not get the compiler to do it for us?

01:19.620 --> 01:25.650
And if you remember the section on compile time programming, there are things called variadic templates

01:25.650 --> 01:30.720
in C++ 11: templates which can take variable numbers and types of arguments.

01:30.750 --> 01:35.790
So the compiler will instantiate these with the correct arguments, depending on how it is called.

01:36.900 --> 01:41.610
And it is also possible to use forwarding references with variadic templates.

01:41.790 --> 01:45.690
So, if necessary, the compiler will instantiate with rvalue references.

01:46.800 --> 01:52.200
So this means we can use perfect forwarding, which is the most efficient way of moving data around

01:52.260 --> 01:53.640
between function calls.

01:55.330 --> 02:00.010
We end up with this rather frightening-looking template function definition!

02:00.160 --> 02:02.380
So let's look instead at how we would call it.

02:02.410 --> 02:05.800
So we have some object of the entity manager.

02:05.920 --> 02:08.080
We call its create() member function.

02:08.590 --> 02:13.430
We give the type of the entity as the type parameter.

02:13.450 --> 02:16.090
So here, we want to create a ball.

02:16.630 --> 02:21.850
So this type parameter T will be replaced by ball when the template is instantiated.

02:22.810 --> 02:25.970
And then the arguments will be the arguments to the ball constructor.

02:25.990 --> 02:29.980
So this will create a ball at position "x" and "y" on the screen.

02:32.220 --> 02:37.590
The compiler will deduce the arguments type from this: x and y are floats.

02:37.590 --> 02:40.050
So this is going through a forwarding reference.

02:40.200 --> 02:45.270
So reference collapsing will apply, and these will emerge as l value references to float.

02:45.570 --> 02:51.510
So this will be instantiated as a function which takes two lvalue references to float and returns

02:51.510 --> 02:53.340
lvalue reference to ball.

02:55.750 --> 03:00.040
The grouped_entities map has all the entities, grouped by their type.

03:00.040 --> 03:06.670
The key of the map depends on the type of the entity. In the runtime type information, we saw that

03:06.700 --> 03:11.200
we can get a hash code for each type, which is guaranteed to be unique.

03:11.350 --> 03:16.930
So this means that each child class of entity will have a different hash code, and we use that as the key

03:16.930 --> 03:17.650
for the map.

03:18.580 --> 03:23.470
And then the value of the map will be a vector of pointers to entity.

03:23.470 --> 03:29.380
So these will be alias pointers to every entity which has the same type as the key.

03:31.170 --> 03:33.210
These are not smart pointers.

03:33.210 --> 03:38.610
They are just traditional C++ pointers, memory addresses. So they do not have any ownership and they are

03:38.610 --> 03:40.860
not connected to the lifetime of the objects.

03:41.100 --> 03:45.750
So if we delete the entities, these pointers will still exist, but they will be invalid.

03:45.900 --> 03:48.540
So we must make sure we do not use those by accident.

03:51.050 --> 03:54.470
So the pseudo-code for the create() member function.

03:54.560 --> 04:00.620
We start off by creating the entity object. we call make_unique(), which will create the object on the

04:00.620 --> 04:03.350
heap, and return a unique_ptr to it.

04:04.400 --> 04:10.160
To get the arguments into the constructor, we use the std::forward() function.

04:10.160 --> 04:15.200
So if we have any arguments which are rvalue references, they will remain rvalues, and not get

04:15.200 --> 04:16.760
converted into lvalues.

04:18.430 --> 04:24.730
So these arguments will be forwarded from the call to create(), into make_unique() and then into the constructor

04:24.730 --> 04:25.570
for the object.

04:25.600 --> 04:27.970
So that minimizes the amount of copying.

04:28.210 --> 04:31.810
Then make_unique() will return a unique_ptr to the object.

04:32.050 --> 04:34.660
We insert that into the all_entities vector.

04:35.530 --> 04:39.610
We get our alias pointer to the entity object, its memory address.

04:40.390 --> 04:43.150
Then we find the hash code of the entity's type.

04:43.420 --> 04:47.620
We get the element in grouped_entities which corresponds to the element's type.

04:48.070 --> 04:52.480
And then we insert our alias pointer into the vector for that element.

04:54.130 --> 04:57.340
And finally, we return the new entity object.

04:58.990 --> 05:06.820
So here is our template function definition, with the variable number of arguments and types, and the forwarding

05:06.820 --> 05:07.510
reference.

05:10.320 --> 05:16.620
So we call this by giving the type of the entity as the parameter and the arguments to the constructor

05:16.620 --> 05:17.520
as the arguments.

05:17.670 --> 05:24.060
So this will create a background object and the constructor arguments will be 0. And similar for

05:24.060 --> 05:25.050
ball and paddle.

05:25.590 --> 05:33.930
So when these functions are instantiated, T will be replaced by background, ball and paddle.

05:34.830 --> 05:39.690
The Args parameter will be replaced by lvalue reference to float and lvalue reference to float.

05:40.140 --> 05:42.330
The actual arguments will be these ones.

05:45.130 --> 05:51.790
We can provide an extra bit of safety at no cost, or no run-time cost any way. We can do a static_assert.

05:51.790 --> 05:57.280
And this will check whether the object we are being asked to create is actually derived from entity.

05:57.850 --> 06:04.360
If it is, then this will be replaced by "true", and the compiler will ignore the assert and continue compiling

06:04.360 --> 06:05.200
the code.

06:06.280 --> 06:12.370
If not, this will be replaced by false and the compiler will emit an error message which includes this

06:12.370 --> 06:13.030
string.

06:13.630 --> 06:20.440
And then we have embedded double quotes in the string so we can use a C++11 raw string, to make that

06:20.440 --> 06:21.850
slightly easier to write.

06:23.680 --> 06:29.680
We then create the actual object. We call make_unique() with the type as the parameter, so make_unique()

06:29.680 --> 06:30.970
with ball as the parameter,

06:30.970 --> 06:36.070
for example. We pass the arguments to create() as the arguments to make_unique().

06:36.940 --> 06:42.730
We put them inside a call to std::forward() to make sure we do not lose any rvalues. forward() requires

06:42.760 --> 06:46.570
a type parameter, so we can put the parameter pack in there.

06:46.690 --> 06:50.890
And then, these three dots will cause the compiler to expand the packs.

06:51.430 --> 06:58.780
So this is going to be replaced by forwarding lvalue reference to float, which takes, say, 0 as argument.

06:59.020 --> 07:04.330
And then another lvalue reference to float, which is forwarded, and takes zero as argument.

07:05.170 --> 07:08.950
And this will return a unique_ptr to the object.

07:09.610 --> 07:14.470
And we can use this as "auto", because the actual type will depend on how this is instantiated.

07:16.470 --> 07:18.150
Then we get our alias pointer.

07:18.990 --> 07:22.190
I do not think I mentioned this before, but unique_ptr

07:22.200 --> 07:28.530
has a member function, get(), which will return a pointer to the object, a C++ pointer with the address.

07:29.130 --> 07:31.830
And generally, this is dangerous, and not a good thing to use.

07:32.400 --> 07:38.520
However, it is safe here, because we are not going to use this to subvert the management of the lifetime,

07:38.520 --> 07:39.540
by unique_ptr.

07:39.570 --> 07:41.670
We are just using this as a kind of reference.

07:42.510 --> 07:47.040
We get the hash code for the entity, so the hash code for ball, for example.

07:47.670 --> 07:51.420
Then we find the element of grouped entities which corresponds to that type.

07:51.660 --> 07:57.360
So this is going to be the element for ball, and then the value will be a vector of pointer aliases

07:57.360 --> 07:58.550
to ball objects.

07:58.620 --> 08:06.180
So we just insert our alias pointer into this vector. And then we insert the unique_ptr into

08:06.180 --> 08:10.080
all_entities. And we have to call std::move() here, because unique_ptrs cannot be copied.

08:10.200 --> 08:11.490
They can only be moved.

08:12.600 --> 08:16.530
And finally, we return the new object, by dereferencing the alias pointer.

08:17.580 --> 08:17.910
OK.

08:17.970 --> 08:19.140
That is it for this video.

08:19.290 --> 08:20.190
I will see you next time.

08:20.190 --> 08:22.590
But until then, keep coding!
