zeus

Unnamed repository; edit this file 'description' to name the repository.
git clone Unknown
Log | Files | Refs

commit c322880ba8d307b301969c3df510ec197b86a5f6
parent e6f07a97f55c4d4f1f6e31bb7012fe60fb21acc1
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Wed, 14 May 2025 17:48:15 +0200

Decorator example

Diffstat:
A decorator_units.cpp | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

1 files changed, 123 insertions(+), 0 deletions(-)


diff --git a/ decorator_units.cpp b/ decorator_units.cpp

@@ -0,0 +1,123 @@

#include <cassert>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>

template <class Callable> class DebugDecorator {
Callable m_callable;
const char *m_s;

public:
template <class F>
DebugDecorator(F &&f, const char *s)
: m_callable(std::forward<F>(f)), m_s(s) {}

template <class... Args> auto operator()(Args &&...args) const {
std::cout << "Invoking " << m_s << std::endl;

using res_t = std::invoke_result_t<Callable, Args...>;
if constexpr (!std::is_same_v<res_t, void>) {
auto res = m_callable(std::forward<Args>(args)...);
std::cout << "Result: " << res << std::endl;
return res;
} else {
m_callable(std::forward<Args>(args)...);
}
}
};

template <class Callable> auto decorate_debug(Callable &&c, const char *s) {
return DebugDecorator<Callable>(std::forward<Callable>(c), s);
}

template <class To, class From> auto move_cast(std::unique_ptr<From> &p) {
#ifndef NDEBUG
auto p1 = std::unique_ptr<To>(dynamic_cast<To *>(p.release()));
assert(p1);
return p1;
#else
return std::unique_ptr<To>(static_cast<To *>(p.release()));
#endif
}

class Unit {
protected:
double m_strength;
double m_armor;

public:
virtual ~Unit() = default;

Unit(double strength, double armor) : m_strength(strength), m_armor(armor) {}

virtual double defense() = 0;
virtual double attack() = 0;

virtual bool hit(Unit &target) { return attack() > target.defense(); }
};

class Knight : public Unit {
protected:
static constexpr double sword_bonus = 2;
static constexpr double plate_bonus = 3;

double m_charge_bonus = 0;

public:
using Unit::Unit;

double defense() override { return m_armor + plate_bonus; }
double attack() override {
const auto res = m_strength + sword_bonus + m_charge_bonus;
m_charge_bonus = 0;
return res;
}

void charge() { m_charge_bonus = 1; }
};

class Ogre : public Unit {
protected:
static constexpr double club_penalty = -1;
static constexpr double leather_penalty = -1;

public:
using Unit::Unit;

double defense() override { return m_armor + leather_penalty; }
double attack() override { return m_strength + club_penalty; }
};

template <class U> class VeteranUnit : public U {
double m_strength_bonus;
double m_armor_bonus;

public:
template <class P>
VeteranUnit(P &&p, double strength_bonus, double armor_bonus)
: U(std::move(*move_cast<U>(p))), m_strength_bonus(strength_bonus),
m_armor_bonus(armor_bonus) {}

double defense() override { return U::defense() + m_armor_bonus; }
double attack() override { return U::attack() + m_strength_bonus; }
};

int main() {
using Unit_ptr = std::unique_ptr<Unit>;
using Knight_ptr = std::unique_ptr<Knight>;
using Ogre_ptr = std::unique_ptr<Ogre>;

Knight_ptr k(new Knight(10, 5));
Unit_ptr o(new Ogre(12, 2));

Knight_ptr vk(new VeteranUnit<Knight>(k, 7, 2));
Unit_ptr vo(new VeteranUnit<Ogre>(o, 1, 9));
Unit_ptr vvo(new VeteranUnit<VeteranUnit<Ogre>>(vo, 1, 9));

vk->hit(*vvo);
vk->charge();
vk->hit(*vvo);

return 0;
}