Video Coming Soon...
50: Simple Template Functions
Cover the nicer new C++20 feature they call "Abbreviated Template Functions" because they think latinized words are more "scientific."
The Code
First an intro to the auto func:
View Source file ex50a.cpp Only
#include <fmt/core.h>
#include <vector>
#include <ranges>
#include <algorithm>
auto add_func(auto a, auto b) {
return a + b;
}
int main() {
using namespace std::ranges;
std::vector<int> the_ints{ 0, 100, 34, 82, 126};
std::vector<float> the_floats{ 0.1f, 100.1f, 34.1f, 82.1f, 126.1f };
// do it manually to see add_func in action
{
int int_res = 0;
for(int i : the_ints) {
int_res += i;
}
fmt::println("manual int result: {}", int_res);
float float_res = 0;
for(float i : the_floats) {
float_res += i;
}
fmt::println("manual float result: {}", float_res);
}
// now use a lambda, which is way easier
{
auto add = [](auto a, auto b) -> auto { return a + b; };
auto int_res = fold_left(the_ints, 0, add);
auto float_res = fold_left(the_floats, 0.0f, add);
fmt::println("ints={}, floats={}", int_res, float_res);
}
// now with a add_func, without add_func<type,type> you get an error
{
auto int_res = fold_left(the_ints, 0, add_func<int,int>);
auto float_res = fold_left(the_floats, 0.0f, add_func<float,float>);
fmt::println("ints={}, floats={}", int_res, float_res);
}
}
The Breakdown
line of code- Description.
The Discussion
Blah blah.
Terrible Idea
Then use auto func to do something stupid in our engine.
See how I did it.
View Source file ex50.cpp Only#include <fmt/core.h>
#include <string>
#include <random>
#include <memory>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <ranges>
using std::shared_ptr, std::make_shared, std::string;
std::random_device RNG;
std::mt19937 GENERATOR(RNG());
struct Combatant {
string name;
int damage{0};
int hp{0};
bool dead{false};
};
/*
* This is a dumb idea, but for education we'll try it.
*/
struct Player {
string name;
int damage{0};
int hp{0};
bool dead{false};
};
using ArenaID = int;
using Arena = std::unordered_map<ArenaID, shared_ptr<Combatant>>;
using CombatQueue = std::vector<size_t>;
int random_number(int from, int to) {
std::uniform_int_distribution rand(from, to);
return rand(GENERATOR);
}
void fight(auto attacker, auto defender) {
using namespace fmt;
int damage = random_number(0, attacker->damage);
if(damage > 0) {
defender->hp -= damage;
println("{} hit {} for {}. {} now has {} hp",
attacker->name, defender->name, damage,
defender->name, defender->hp);
} else {
println("{} missed {}!",
attacker->name, defender->name);
}
if(defender->hp <= 0) {
fmt::println("{} died. {} wins! {} has {} HP",
defender->name, attacker->name,
attacker->name, attacker->hp);
}
}
void fill_arena(Arena& arena, int combatants) {
using namespace std::ranges;
for(auto arena_id : views::iota(0, combatants)) {
string name = fmt::format("Giant Rat #{}", arena_id);
int hp = random_number(5,20);
int damage = random_number(2,10);
arena.try_emplace(arena_id, make_shared<Combatant>(name, damage, hp, 1.4f));
}
}
bool combat_active(Arena& arena, shared_ptr<Combatant> player) {
return !arena.empty() && player->hp > 0;
}
CombatQueue queue_combat(Arena& arena) {
using namespace std::ranges;
auto queue = arena | views::keys | to<CombatQueue>();
shuffle(queue, GENERATOR);
return queue;
}
void run_combat(auto combat_a, auto combat_b) {
int winner = random_number(0,1);
// need to explain this
auto attacker = winner == 1 ? combat_a : combat_b;
auto defender = winner == 0 ? combat_a : combat_b;
fight(attacker, defender);
if(defender->hp <= 0) {
defender->dead = true;
}
}
void cull_the_dead(Arena& arena) {
using namespace std::ranges;
auto is_dead = [&](auto& tuple) -> bool { return tuple.second->dead; };
auto dead = arena | views::filter(is_dead) | views::elements<0> | to<CombatQueue>();
for(auto id : dead) {
arena.erase(id);
}
}
int main(int argc, char* argv[]) {
using namespace std::ranges;
Arena arena;
auto player = make_shared<Combatant>("Player", 20, 50, 1.4f);
fill_arena(arena, 10);
// now with multiple combatants we have to keep going until the player dies
while(combat_active(arena, player)) {
auto whos_next = queue_combat(arena);
for_each(whos_next, [&](auto enemy_id) {
run_combat(player, arena[enemy_id]);
});
cull_the_dead(arena);
}
if(arena.empty()) {
fmt::println("The Player Wins! You defeated all of the rats.");
} else {
fmt::println("You Died! The Rats Win.");
}
}
Further Study
- Do this next.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.