I have a templated type that follows the Observer Pattern.
template <typename T>
class Subject {
public:
template <template<typename> typename Observer>
void AddObserver(Observer<T>& observer)
{
observers.push_back([&](const T& value) { observer.OnEvent(value); });
}
void Event(const T& value) const
{
for (auto& o : observers) {
o.OnEvent(value)
}
}
private:
std::vector<std::function<void(const T&)>> observers;
};
The above works great but only so long as the Observer
is a templated type. So I can modify the template signature and accept any type, regardless of whether they are templated.
template <typename Observer>
void AddObserver(Observer& observer)
{
observers.push_back([&](const T& value) { observer.OnEvent(value); });
}
But now there aren't any compile time checks on type matching between the Subject and the Observer. (EDIT it has been pointed out that there is no guarantee that Observer<T>
actually uses T
in its OnEvent
function signature)
I'd like to add a static cast to the function that checks that the two types Subject<**T**>
and the Observer::OnEvent(const **T**&)
are the same, to prevent automagic type conversions. Something like the below.
static_cast(std::is_same<T, **type_of_first_param**(Observer::OnEvent)>::value, "Subject/Observer type mistatch.");
If I cannot extract the type from the function signature, perhaps I can construct a function signature to which I can compare it?
static_cast(std::is_same<**Observer::MadeUpFunc(const T&)**, Observer::OnEvent>::value, "Subject/Observer type mistatch.");
I'm investigating this QA, and I'm pretty sure it holds the answer I need, just need some time and perhaps guidance to figure it out.
I have tried the following, but get the compile time error
typename specifier refers to non-type member 'OnEvent' in 'Observer'
static_assert(std::is_same<typename Observer::OnEvent, void (Observer::*)(const T&...)>::value, "");
It's not clear to me what restrictions you want here. The most restrictive would be to enforce something like:
static_assert(std::is_same_v<decltype(&Observer::OnEvent),
void (Observer::*)(const T&) const>);
where the OnEvent
function couldn't be a template
, it couldn't return anything aside from void
, it would be required to be marked const
, and it would have to take const T&
, even if T
is int
.