Hello again! In this video,
we are going to continue looking at unions.
In the last video, we saw that we can use a tagged union to make things a bit safer.
However, this still requires the programmer to check that a member is in use before they access it.
And also, when the programmer brings a member into use, they have to remember to set the type.
So there is still scope for human error.
And besides, this is a very "C language" way of doing things. We are in C++, so why not use C++ features?
And one of the things we could do, is to wrap this tagged union inside a class.
So we have a class where the union is a private member.
There are public member functions of the class, which are the only way to access the union.
These functions will make sure that the tagged member is set and checked correctly, so the right type
is always used.
If we try to do something which is invalid, like accessing a member which is not in use, the member
functions can perform error handling. So they could return a null pointer, for example, or throw an
exception.
And this means that the union can only be used correctly.
It is not possible to use it in the wrong way.
So let's try that out.
We have the same union that we had in the previous video.
This has the member with the token type, which is an enumeration of the allowed types.
We create a class which has an object of this union as a member.
Then we have member functions which set and get the members.
I have not written all of them, but I am sure you can work out how to do it!
For example, with the member function which will set the character member.
This first sets the token type to character, and then it brings the character member into use.
The member function which gets the double member.
This will first check that the double member is in use, and it only returns the double member if it is
in use.
Otherwise it throws an exception.
So this is the standard invalid argument, from the exception hierarchy.
In the main function, we create an object of this class. We call set_character().
So this rule sets the token type to character, and then bring the character member into use.
We then call the get_double() member function.
This will return the double member, if it is in use.
Otherwise, it will throw an exception.
So we have a try-catch block which will handle the exception.
And then we go.
We get an exception, and the double member is not in use.
In C++ 17, there is now a type in the library which will do all this for us.
It is called "variant".
So this will do all the things that a wrapped, tagged union will do.
It is type safe.
You are allowed to have different members, or "alternatives", as they are called, which are the same type.
Which is not possible with a union.
The variant will automatically call the constructors and destructors for the members, which
the union will not do.
You have to call them yourself with a union.
And all in all, it is simpler and safer to use.
And you do not have to write all that code!
Variant is a templated type. It is defined in the header.
The parameters are the types of the members, or rather, alternatives. (I must get used to saying that!)
So this variant will have alternatives which are character, int and double.
So that is the same as having the union that we had earlier on.
To use a variant we just assign to it.
We do not need to assign to the members. The compiler
will deduce which alternative we need.
So if we assign to it and give a character argument, then this will bring the character member into use.
To access the data,
we call the get() function, and this works just like it does with tuples.
So we can give the type as the parameter, and then we give the variant as the arguments.
So this will retrieve the character member if it is in use. And if it is not in use, it will throw an exception.
We can also get by index, and that is useful if there are alternatives which have the same type.
So if we put zero as the parameter, that will gets the alternative which has index zero, which is
the character.
If we want to check whether an alternative is in use,
there is the holds_alternative() function.
This takes the type as the parameter.
So if we want to know if double is in use, we put that as the parameter. And this will return true
if it is in use and false if it is not.
So here is an example.
We include the variant header.
We create a variant object which has the type parameters for char, int and double.
We assign to a character, so that will bring the character member into use.
We called holds_alternative() to find out if the double member is in use. And if it is, then we can call get().
Okay.
So the double member is not in use.
Right.
So why is this useful?
What would you use a union or variant for?
One application that we have already mentioned is parsing.
If you are passing source code where a variable could have one of several types.
Also, if you're parsing configuration files such as .INI files on Windows, where the parameters
can only be a string, a number or a Boolean.
That is quite a common way of writing them.
You can also use them for implementing your own language.
There are some languages which have a thing called "duck typing", which means that an object can have
several types, and the type which it has depends on how it is used.
You can also use them for return values, if you want to return different types.
So he could, for example, have a computation which can have outcomes with different types.
You can also use a union or variant for error handling.
So if the operation succeeds, you can return an object which has the result.
And if it fails, you can return an error code.
And finally, this is another way to do polymorphism. And it is actually possible to implement virtual
functions using unions and variant, if you are completely insane!
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!