etl/docs/tutorials/delegate-service.md
Roland Reichwein 08b9c371a0
Various fixes and cleanup (#1507)
* Fix histogram test

Use different Start index so skipped start_index copy would not get
unnoticed in the test.

* Fix several test_vector_* for valid data

Instead of comparing the moved-from data which is invalid, compare with
the valid initial_data.

* Fix test_vector to check for the correct iterator position after erase

This was done before, but a recent change dropped it.

* Fix bit_stream: Guard nbits > 0 to prevent division by zero

* Cleanup

---------

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
2026-07-20 11:19:41 +02:00

4.7 KiB

title
delegate_service

The following code can be found in examples\FunctionInterruptSimulation-Delegates

This code demonstrates using the delegate service for five example ARM interrupts.

Timer1 interrupt is handled by an instance of class Timer. The member callback function is wrapped by the most efficient version of etl::delegate in which all of the information it requires is known at compile time.

Timer2 interrupt is handled by a global function.

Timer3 has no entry in the callback service and will therefore trigger execution of the unhandled handler.

USART1 and USART2 interrupts are handled by instances of Uart.
Their callbacks are defined within the class and are initialised in the Uart constructor.

#include <iostream>

#include "etl/delegate.h"
#include "etl/delegate_service.h"

enum VectorId
{
  TIM1_CC_IRQ_HANDLER = 42,
  TIM2_IRQ_HANDLER    = 43,
  TIM3_IRQ_HANDLER    = 44,
  USART1_IRQ_HANDLER  = 52,
  USART2_IRQ_HANDLER  = 53,
  VECTOR_ID_END,
  VECTOR_ID_OFFSET    = TIM1_CC_IRQ_HANDLER,
  VECTOR_ID_RANGE     = VECTOR_ID_END - VECTOR_ID_OFFSET
};

using InterruptVectors = etl::delegate_service<VECTOR_ID_RANGE, VECTOR_ID_OFFSET>;

// Ensure that the callback service is initialised before use.
InterruptVectors& GetInterruptVectorsInstance()
{
  static InterruptVectors interruptVectors;

  return interruptVectors;
}

extern "C"
{
  InterruptVectors& interruptVectors = GetInterruptVectorsInstance();

  // Function called from the timer1 interrupt vector.
  void TIM1_CC_IRQHandler()
  {
    interruptVectors.call<TIM1_CC_IRQ_HANDLER>();
  }

  // Function called from the timer2 interrupt vector.
  void TIM2_IRQHandler()
  {
    interruptVectors.call<TIM2_IRQ_HANDLER>();
  }

  // Function called from the timer3 interrupt vector.
  void TIM3_IRQHandler()
  {
    interruptVectors.call<TIM3_IRQ_HANDLER>();
  }

  // Function called from the usart1 interrupt vector.
  void USART1_IRQHandler()
  {
    interruptVectors.call<USART1_IRQ_HANDLER>();
  }

  // Function called from the usart2 interrupt vector.
  void USART2_IRQHandler()
  {
    interruptVectors.call<USART2_IRQ_HANDLER>();
  }
}

//********************************
// Timer driver.
//********************************
class Timer
{
public:

  // Handler for interrupts from the timer.
  void InterruptHandler(const size_t id)
  {
    std::cout << "Timer interrupt (member) : ID " << id << "\n";
  }
};

//********************************
// Free function timer driver.
//********************************
void FreeTimerInterruptHandler(const size_t id)
{
  std::cout << "Timer interrupt (free)   : ID " << id << "\n";
}

//********************************
// UART driver.
//********************************
class Uart
{
public:

  // Constructor.
  Uart(int port_id, size_t interruptId)
    : port_id(port_id)
    , callback(etl::delegate<void(size_t)>::create<Uart, &Uart::InterruptHandler>(*this))
  {
    GetInterruptVectorsInstance().register_delegate(interruptId, callback);
  }

  // Handler for interrupts from the UART.
  void InterruptHandler(const size_t id)
  {
    std::cout << "UART" << port_id << "                    : ID " << id << "\n";
  }

  etl::delegate<void(size_t)> callback;

  int port_id;
};

void UnhandledInterrupt(const size_t id)
{
  std::cout << "Unhandled Interrupt      : ID " << id << "\n";
}

// Declare the driver instances.
Timer timer;
Uart  uart1(0, USART1_IRQ_HANDLER);
Uart  uart2(1, USART2_IRQ_HANDLER);

// Declare a global callback for the timer.
// Uses the most efficient callback type for a class, as everything is known at compile time.
etl::delegate<void(size_t)> timer_member_callback = 
    etl::delegate<void(size_t)>::create<Timer, timer, &Timer::InterruptHandler>();

// Declare the callbacks for the free functions.
etl::delegate<void(size_t)> timer_free_callback =     
    etl::delegate<void(size_t)>::create<FreeTimerInterruptHandler>();

etl::delegate<void(size_t)> unhandled_callback  = 
    etl::delegate<void(size_t)>::create<UnhandledInterrupt>();

//********************************
// Test it out.
//********************************
int main()
{
  // Setup the callbacks.
  InterruptVectors& interruptVectors = GetInterruptVectorsInstance();

  interruptVectors.register_delegate<TIM1_CC_IRQ_HANDLER>(timer_member_callback);
  interruptVectors.register_delegate<TIM2_IRQ_HANDLER>(timer_free_callback);
  interruptVectors.register_unhandled_delegate(unhandled_callback);

  // Simulate the interrupts.
  TIM1_CC_IRQHandler();
  TIM2_IRQHandler();
  USART1_IRQHandler();
  USART2_IRQHandler();
  TIM3_IRQHandler(); // Unhandled!

  return 0;
}

Output

Timer interrupt (member) : ID 42
Timer interrupt (free)   : ID 43
UART0                    : ID 52
UART1                    : ID 53
Unhandled Interrupt      : ID 44