In C is there a way, using raw pointers or otherwise to trigger some action when the pointed to object changes?
Scenario:
class A
{
double var;
B var2 {&var};
}
class B
{
double* pVar;
B (double* _var ) { pVar = _var};
}
I have functions inside class B that will get called whenever member variable var changes value. At the moment I would need these functions to be public so they can be called manually from class A, this could be achieved by using a setter for var. If I wanted to keep the function inside class B private (as it is called by other events internal to class B) what are my options?
CodePudding user response:
This is an example of the observer pattern. So what you need is to trigger a function call to another object when the value of your object is changed through a setter method.
#include <functional>
#include <iostream>
//-----------------------------------------------------------------------------
// the class with the member variable that can change
// and to which another class can react.
// This is a simple example where only one "callback" function
// can be registered. In the typical observer pattern
// there can be multiple callbacks registered.
class Observee
{
public:
// set notification function to a function that does nothing
// "null strategy pattern"
Observee() :
m_notify_observer_fn{ [](int) {} }
{
}
// let another object pass in a function that will be called
// when the value is changed
void OnValueChanged(std::function<void(int)> notify_observer_fn)
{
m_notify_observer_fn = notify_observer_fn;
}
// to change the member value AND notify the other object
// that the value has changed we need a setter function.
void set_value(int value)
{
// check if the value really has changed
if (m_value != value)
{
// set the member value
m_value = value;
// then notify the observer of the new value
// by calling the notification function
m_notify_observer_fn(m_value);
}
}
private:
std::function<void(int)> m_notify_observer_fn;
int m_value{};
};
//-----------------------------------------------------------------------------
// The class that wants to get a notification when the value
// of the other class changes (role is an Observer)
class Observer
{
public:
explicit Observer(Observee& observee)
{
// Set the callback in the observee to the OnValueChanged
// function of this object (the function passed is called a lambda funtion)
observee.OnValueChanged([this](int value)
{
OnValueChanged(value);
});
}
private:
void OnValueChanged(int value)
{
std::cout << "Value changed to " << value << "\n";
}
};
//-----------------------------------------------------------------------------
int main()
{
// make instances of both classes.
Observee observee;
Observer observer{ observee };
// now set the value
// this will change the member in observee
// and then call the method in the observer for you
observee.set_value(42);
return 0;
}
CodePudding user response:
Question : If I wanted to keep the function inside class B private (as it is called by other events internal to class B) what are my options?
You can call a private function of class B while coding class A using friend attribute.
class B
{
friend class A;
private:
void foo() { std::cout << "using foo." << std::endl; }
};
class A
{
private:
B b;
public:
void bar(){ b.foo(); }
};
int main()
{
A a;
a.bar();
return 0;
}
About the callback to be call when a double variable change its value: No you can't do it with a raw pointer.
You got at least two ways of doing it. The first way is what you outlined : use a setter function. The second is to make a class that own the value and that overloading operator= is able to call the callback. I'll sketch something here to make you understand better:
template<class T>
class Owner{
using FuncType = std::function<void(Owner<T>&)>;
public:
Owner(){}
Owner(const T& init){
_var = init;
}
Owner(const Owner<T>& init){
_var = init;
}
operator T(){
return _var;
}
auto& operator =(const T& rvalue){
_var = rvalue;
_on_change();
return *this;
}
auto& operator =(const Owner<T>& rvalue){
_var = rvalue;
_on_change();
return *this;
}
const T& get() const { //don't make it non const or
//you will lose ownership to value of _var
return _var;
}
void set(const T& val){
_var = val;
_on_change();
}
void set(const Owner<T>& val){
_var = val;
_on_change();
}
void set_handler(FuncType func)
{
_func = func;
}
private:
void _on_change(){
if(_func)
_func(*this);
}
private:
T _var{};
FuncType _func{};
};
int main()
{
Owner<double> var{};
var.set_handler([](Owner<double>& ch){
std::cout << "Value changed: " << (double) ch << std::endl;
});
var = 1.0;
return 0;
}