Metaprogramming Academy: Type based referencing
Posted: November 17th, 2017, 11:22 am
Morning class!
Today, we'll be going into the fun things you can do with templates! Now even though Chili has already covered the basics of templates, the world of template metaprogramming is far more vast than one video can cover! Today, we are going to explore ways templates can simplify code through type based referencing. Let's say that you want to create a Graphics class that can make different graphic objects and return their ids to the user. Likewise, those graphic objects can be returned to be reused by other users. Without any template metaprogramming, it'll usually look like so:
As you can see, there is a lot of boilerplate code to write just to add another type that Graphics can create. Most of the code for each type is practically the same without much deviations. At first, it would seem easy to express these functions using one function template like:
...and you would be right except we need a way for the template function to use the vector [sprites_] for [Type = Sprite] and [texts_] for [Type = text]. Fortunately, C++ has an in built class that allows us to do this easily.
Introducing: Tuples!
You can think of them as a fancy struct for template metaprogramming. Unlike structs though, members of a tuple can be referenced through indices or types(if the type is unique in the tuple that is) rather than names. This makes them perfect for our current situation. With tuples, along with the help of a helper class...
We can redesign the Graphics class with a few more Graphic Objects added in.
Admittedly, this example is very bare bones and there are more ways to simplify it(having an RAII class to delete the resource automatically for one) but I hope that it shows you the power of tuples. Tuples are the lifeblood of template metaprogramming(and metaprogramming in general really) and you'll learn to love them as you try more and more template metaprogramming techniques. This is merely the start of my lessons and as we continue on, I hope all of you would learn to use more template metaprogramming in your code.
Today, we'll be going into the fun things you can do with templates! Now even though Chili has already covered the basics of templates, the world of template metaprogramming is far more vast than one video can cover! Today, we are going to explore ways templates can simplify code through type based referencing. Let's say that you want to create a Graphics class that can make different graphic objects and return their ids to the user. Likewise, those graphic objects can be returned to be reused by other users. Without any template metaprogramming, it'll usually look like so:
Code: Select all
class Graphics
{
public:
using Id = size_t;
class Sprite
{
public:
Sprite ( ) = default;
void Render ( ) const
{
std::cout << "Sprite rendered!" << '\n';
}
};
class Text
{
public:
Text ( ) = default;
void Render ( ) const
{
std::cout << "Text rendered!" << '\n';
}
};
Graphics ( ) = default;
Id CreateSprite ( )
{
//recycle unused sprites
if ( unused_sprites_.empty ( ) )
{
const auto id = sprites_.size ( );
sprites_.emplace_back ( );
return id;
}
else
{
const auto id = unused_sprites_.back ( );
unused_sprites_.pop_back ( );
return id;
}
}
Id CreateText ( )
{
//recycle unused texts
if ( unused_texts_.empty ( ) )
{
const auto id = texts_.size ( );
texts_.emplace_back ( );
return id;
}
else
{
const auto id = unused_texts_.back ( );
unused_texts_.pop_back ( );
return id;
}
}
void DeleteSprite ( const Id id )
{
//save the sprite for reuse
unused_sprites_.push_back ( id );
//reset the sprite
sprites_ [ id ] = { };
}
void DeleteText ( const Id id )
{
//save the text for reuse
unused_texts_.push_back ( id );
//Reset the text
texts_ [ id ] = { };
}
Sprite& GetSprite ( const Id id )
{
return sprites_ [ id ];
}
Text& GetText ( const Id id )
{
return texts_ [ id ];
}
private:
std::vector<Sprite> sprites_;
std::vector<Id> unused_sprites_;
std::vector<Text> texts_;
std::vector<Id> unused_texts_;
};
Code: Select all
template<typename Type>
Create( );
Introducing: Tuples!
Code: Select all
std::tuple<int, double, std::string> tuple = std::make_tuple ( 12, 3., "#LeninDidNothingWrong" );
std::cout << std::get<int> ( tuple ) << '\n';
std::cout << std::get<double> ( tuple ) << '\n';
std::cout << std::get<2> ( tuple ) << '\n';
Code: Select all
template<typename Resource>
class ResourceManager
{
public:
using ResourceContainer = std::vector<Resource>;
using Id = typename ResourceContainer::size_type;
using IdContainer = std::vector<Id>;
ResourceManager ( ) = default;
Id Create ( )
{
//recycle unused resources
if ( unused_resources_.empty ( ) )
{
const auto id = resources_.size ( );
resources_.emplace_back ( );
return id;
}
else
{
const auto id = unused_resources_.back ( );
unused_resources_.pop_back ( );
return id;
}
}
void Delete ( const Id id )
{
//save the resource for reuse
unused_resources_.push_back ( id );
//reset the resource
resources_ [ id ] = { };
}
Resource& Get ( const Id id )
{
return resources_ [ id ];
}
private:
ResourceContainer resources_;
IdContainer unused_resources_;
};
Code: Select all
class Graphics
{
public:
class Sprite
{
public:
Sprite ( ) = default;
void Render ( ) const
{
std::cout << "Sprite rendered!" << '\n';
}
};
class Text
{
public:
Text ( ) = default;
void Render ( ) const
{
std::cout << "Text rendered!" << '\n';
}
};
class Mesh
{
public:
Mesh ( ) = default;
void Render ( ) const
{
std::cout << "Mesh rendered!" << '\n';
}
};
class Isosurface
{
public:
Isosurface ( ) = default;
void Render ( ) const
{
std::cout << "Isosurface rendered!" << '\n';
}
};
Graphics ( ) = default;
template<typename Type>
auto Create ( )
{
return std::get<ResourceManager<Type>> ( resource_managers_ ).Create ( );
}
template<typename Type>
void Delete ( const typename ResourceManager<Type>::Id id )
{
std::get<ResourceManager<Type>> ( resource_managers_ ).Delete ( id );
}
template<typename Type>
auto& Get ( const typename ResourceManager<Type>::Id id )
{
return std::get<ResourceManager<Type>> ( resource_managers_ ).Get ( id );
}
private:
std::tuple<
ResourceManager<Sprite>,
ResourceManager<Text>,
ResourceManager<Mesh>,
ResourceManager<Isosurface>
> resource_managers_;
};