Delegates and the Observer Pattern

Observer Pattern Without Dynamic Allocation

Is it possible to implement the observer pattern without dynamic memory allocation?

Abstract art representing programming

Yes, you can implement the observer pattern without dynamic allocation. This can be particularly important in embedded systems or performance-critical code. Let's explore several approaches.

Fixed-Size Array Approach

Using a fixed array to store observers:

#include <array>
#include <iostream>

class Player {
 public:
  static constexpr size_t MaxObservers{8};
  using DamageCallback = void (*)(int NewHealth);

  int AddObserver(DamageCallback Cb) {
    for (size_t i = 0; i < MaxObservers; ++i) {
      if (!Observers[i]) {
        Observers[i] = Cb;
        return i;
      }
    }
    return -1;  // Array full
  }

  void RemoveObserver(int Idx) {
    if (Idx >= 0 && Idx < MaxObservers) {
      Observers[Idx] = nullptr;
    }
  }

  void TakeDamage(int Damage) {
    Health -= Damage;
    for (auto& Obs : Observers) {
      if (Obs) Obs(Health);
    }
  }

 private:
  std::array<
    DamageCallback, MaxObservers> Observers{};
  int Health{100};
};

Static Observer Registration

Using compile-time registration with static storage:

#include <array>

template <typename T, size_t MaxObservers>
class StaticSubject {
public:
  using Callback = void(*)(const T& Event);

  static int RegisterObserver(Callback Cb) {
    for (size_t i = 0; i < MaxObservers; ++i) {
      if (!Observers[i]) {
        Observers[i] = Cb;
        return i;
      }
    }
    return -1;
  }

  static void UnregisterObserver(int Idx) {
    if (Idx >= 0 && Idx < MaxObservers) {
      Observers[Idx] = nullptr;
    }
  }

protected:
  static void NotifyObservers(const T& Event) {
    for (auto& Obs : Observers) {
      if (Obs) Obs(Event);
    }
  }

private:
  static inline std::array<
    Callback, MaxObservers> Observers{};
};

struct PlayerEvent {
  int NewHealth;
};

class
  Player : public StaticSubject<
    PlayerEvent, 8> {
public:
  void TakeDamage(int Damage) {
    Health -= Damage;
    NotifyObservers({Health});
  }

private:
  int Health{100};
};

Object Pool Approach

For more flexibility without dynamic allocation:

#include <array>
#include <bitset>

template<typename T, size_t PoolSize>
class ObjectPool {
public:
  T* Acquire() {
    for (size_t i = 0; i < PoolSize; ++i) {
      if (!InUse[i]) {
        InUse[i] = true;
        return &Objects[i];
      }
    }
    return nullptr;
  }

  void Release(T* Ptr) {
    if (Ptr) {
      size_t Idx = Ptr - Objects.data();
      if (Idx < PoolSize) {
        InUse[Idx] = false;
      }
    }
  }

private:
  std::array<T, PoolSize> Objects;
  std::bitset<PoolSize> InUse;
};

class Observer {
// Observer implementation
};

class ObserverSystem {
public:
  Observer* CreateObserver() {
    return Pool.Acquire();
  }

  void DestroyObserver(Observer* Obs) {
    Pool.Release(Obs);
  }

private:
  static constexpr size_t MaxObservers{32};
  ObjectPool<Observer, MaxObservers> Pool;
};

Key considerations for allocation-free observers:

  • Determine maximum number of observers at compile time
  • Handle the "out of space" scenario gracefully
  • Consider using a pool for more complex observer types
  • Be mindful of stack size when using fixed arrays
  • Consider using static storage for global observers

The main tradeoff is flexibility vs. memory usage:

  • Fixed arrays are simple but waste memory if underused
  • Static registration is efficient but less flexible
  • Object pools provide a middle ground

Choose based on your specific needs:

  • Embedded systems: Fixed arrays or static registration
  • Game development: Object pools for better memory usage
  • Performance-critical code: Any approach that avoids allocation

Answers to questions are automatically generated and may not have been reviewed.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 53 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved