Skip to content

Instantly share code, notes, and snippets.

@daniel5151
Last active February 3, 2022 09:07
Show Gist options
  • Save daniel5151/71d685a549365662141956aba9cc649f to your computer and use it in GitHub Desktop.
Save daniel5151/71d685a549365662141956aba9cc649f to your computer and use it in GitHub Desktop.
Generic Callback Manager
#pragma once
#include <vector>
#include <cstddef>
/**
* Generic Callback Manager
*
* @tparam ...cb_args Types of callback arguments (aside from userdata)
*/
template <typename ...cb_args>
class CallbackManager {
public:
template <typename udata_t>
using cb_t = void(*)(udata_t* userdata, cb_args...);
private:
struct cb_node {
void* userdata;
cb_t<void> cb;
};
std::vector<cb_node> _cbs;
public:
/**
* Add a callback with specified userdata
* @param userdata returned as first-parameter of callback
* @param function callback of type void cb(void* userdata, cb_args...);
* @return callback successfully added
*/
bool add(void* userdata, cb_t<void> function) {
_cbs.push_back(cb_node {userdata, function});
return true;
}
/**
* Remove a userdata-callback pair from the list of callbacks
* @param userdata userdata to search for
* @param function callback to search for
* @return callback successfully removed
*/
bool remove(void* userdata, cb_t<void> function) {
for (size_t i = 0; i < _cbs.size(); i++) {
if (_cbs[i].cb != function || _cbs[i].userdata != userdata)
continue;
for (size_t j = i; j < _cbs.size() - 1; j++)
_cbs[j] = _cbs[j + 1];
_cbs.pop_back();
return true;
}
return false;
}
/**
* @param args... execute callbacks with specified argument
*/
void run(cb_args... args) const {
for (const cb_node& cb : _cbs)
cb.cb(cb.userdata, args...);
}
// ergonomic templates: add/remove callbacks with non void* userdata type
/**
* Add a callback with specified userdata
* @param userdata returned as first-parameter of callback
* @param function callback of type void cb(udata_t* userdata, cb_args...);
* @return callback successfully added
*/
template <typename udata_t>
bool add(udata_t* userdata, cb_t<udata_t> function) {
return add(userdata, (cb_t<void>)function);
}
/**
* Remove a userdata-callback pair from the list of callbacks
* @param userdata userdata to search for
* @param function callback to search for
* @return callback successfully removed
*/
template <typename udata_t>
bool remove(udata_t* userdata, cb_t<udata_t> function) {
return remove(userdata, (cb_t<void>)function);
}
};
@daniel5151
Copy link
Author

Example Usage:

#include <cstdio>
#include "callback_manager.h"

// stateless callback
void print(void* _, int x) {
  printf("%d ", x);
}

// stateful callback
void accumulate_print(int* a, int x) {
  *a += x;
  printf("%d ", *a);
}

// member-function callback
class ProductPrinter {
private:
  int product = 1;

  void product_print(int x) {
    product *= x;
    printf("%d ", product);
  }

  static void product_print_cb(ProductPrinter* self, int x) {
    self->product_print(x);
  }
public:
  ProductPrinter(CallbackManager<int>& val_producer) {
    val_producer.add(this, product_print_cb);
  }
};

int main() {
  CallbackManager<int> val_producer;

  // stateless callback
  val_producer.add(nullptr, print);

  // stateful callback
  int sum = 0; // userdata
  val_producer.add(&sum, accumulate_print);

  // member-fn callback (see class impl)
  ProductPrinter p (val_producer);

  // start running producer
  for (int i = 1; i < 6; i++) {
    val_producer.run(i);
    printf("\n");
  }

  // output:
  // 1 1 1
  // 2 3 2
  // 3 6 6
  // 4 10 24
  // 5 15 120

  return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment