EDIT:
- I want to implement an subject - observer pattern such that I can call
notify<SUBJECT>()
from anywhere and manyupdate<SUBJECT>()
fuctions get called - A subject is a struct type. There can be several of these subjects
- I want to be able to put an observer update function anywhere in the code (in different compilation units)
- The notify function needs to known which update functions to call at compile-time
- It would be good if the notify functions can be connected to the update functions automatically
I am trying to implement a kind of Observer pattern, the notification mechanism. I currently use a run-time implementation that allows one to attach itself to a vector. However, all "attach" calls are made during startup so it can also be done (and I want in to be done) at compile-time.
- My first idea was without the int template parameter which leads to redefinition error from the linker. It would be nice if you can tell the linker to keep all of them and call all of them, but I'm pretty sure that this cannot be done.
- Now I use weak linkage and an int template parameter to make the methods unique. However, that is not as comfortable as I want it. Having to remember all the numbers across the whole codebase. At least, I get hard errors when I use the number twice.
- I'm not so deep into template metaprogramming, but I think it gets complicated when it comes to different compilation units (but I can also switch to a single compilation unit)
- I'd like to avoid boost but C++17 is perfectly fine.
This is the common header:
struct Test {
int value;
};
namespace Observer
{
template<typename T, int N>
void update(decltype(T::value));
/*
* This ìs a function dummy which can be overloaded with the
* real implementation. The linker overrides them and kicks out
* the unoverridden functions since they do nothing.
*/
template<typename T, int N>
void __attribute__((weak)) update(decltype(T::value)) {}
}
template<typename T>
void notify(decltype(T::value) value)
{
Observer::update<T, 1>(value, value != old);
Observer::update<T, 2>(value, value != old);
Observer::update<T, 3>(value, value != old);
}
Let's say we have two compile units:
template<>
void Observer::update<Test, 1>(decltype(Test::value) value)
{
std::cout << "update: " << value << std::endl;
}
template<>
void Observer::update<Test, 2>(decltype(Test::value) value)
{
std::cout << "update: " << value << std::endl;
}
I need to get rid of the numeric iteration in different compile units but can still have multiple implementations of the update function of the same type. All "connected" at compile-time to be called at run-time.