我正在研究一个表示正常数据值的对象,该对象利用函数式反应式编程,当依赖值发生变化时会改变它们的值。我的意思是,假设你有一个 var3 = var1 + var2; 然后当您更改 var1 的值时,var3 的值会自动更新。在 C++ 中,这很困难,但是在某个地方的另一个工作线程中调用了更新函数,你可以让它看起来是功能反应的。
所以这是我这样做的方法。我创建了一个名为 Reactive 的模板对象,其中可以包含任何类型,然后重载它的运算符,这样当这些反应中的两个被加在一起时,不仅结果值等于它们的总和,而且 lambda 是made 将操作存储在 std::function 中,以后可以在任何时候调用它的更新函数时再次调用它来更新结果的值。
出现了几个问题。如果依赖值之一被破坏怎么办。即使生成的 Reactive 仍然具有它认为有效的 lambda,但 lambda 使用的参数不再存在。为了解决这个问题,我求助于 boost::signals2 来建立一个信号和槽系统,以通知结果者他们的任何一个依赖者被破坏。当结果接收到信号时,它的影响函数被清空并且不会在更新时被调用。
为了让 Reactives 能够执行 + 操作,必须创建一个临时的响应式,它具有自己的信号,然后 = 运算符必须重载以将临时中的数据移动到生成的响应式。然而,信号不能被复制。我通过在 std::unique_ptr 中包含 destruct 信号来避开这个问题,当 operator = 收到 Reactive && 时使用 std::move。危机化解了。
现在这是我卡住的地方。后来我意识到,尽管移动构造很好,但仍然无法复制我的 Reactives 之一,因为可以说 var3 = var1 + var2; 然后你这样做: var4 = var3; 然后不知何故 var1 和 var2 中的破坏信号需要一种方法来通知 var4 它们已被破坏。我最终想出的是设置它,以便我有一个名为 Proxy 的子对象,它是一个函子,包含一个 boost::signals2::signal,然后每个对象都会有一个包含在 std::shared_ptr . 如果 Reactive 有对该代理的引用,那么它将它的通知销毁方法连接到该代理。然后依赖者将信号附加到该代理。当代理被调用时,它也会调用它的所有连接。
问题是,将代理连接到依赖信号需要代理具有复制构造函数,或者至少,这是 msvc 给我的错误。显然 boost::signals2::signal::connect 使用它的复制构造函数,它不能因为代理本身包含一个信号。我给你所有这些信息,因为我仍然不确定这是否是最好的解决方案。我选择信号和插槽是因为我对它们最熟悉,但是如果您有更好的解决方案,请指出。否则,请帮助我避免此错误。
顺便说一句,Slot 只是使 Unreact() 函数成为仿函数的一种方式,每个 Reactive 都是唯一的。
这是对象:
template<class T>
class Reactive
{
template<class H>
friend class Reactive;
class Slot : public boost::signals2::trackable
{
public:
Slot(std::function<void()> & func) :
m_Func(func)
{}
void operator()()
{m_Func();}
private:
std::function<void()> m_Func;
};
class Proxy : public boost::signals2::trackable
{
Proxy(const Proxy & s);
Proxy & operator=(const Proxy & s);
public:
Proxy(){}
void operator()()
{m_Informer();}
void attach(Slot & m_Unreacter)
{m_Informer.connect(m_Unreacter);}
private:
boost::signals2::signal<void()> m_Informer;
};
public:
~Reactive()
{
(*m_SendDestruct)();
}
Reactive() :
m_SendDestruct(new boost::signals2::signal<void()>),
m_Proxy(new Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(nullptr)
{
m_Proxy->attach(m_ReceiveDestruct);
}
template<class H>
Reactive(const H & data) :
m_SendDestruct(new boost::signals2::signal<void()>),
m_Proxy(new Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(nullptr),
m_Data(data)
{
m_Proxy->attach(m_ReceiveDestruct);
}
Reactive(const Reactive & reac) :
m_SendDestruct(new boost::signals2::signal<void()>),
m_Proxy(reac.m_Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(reac.m_Affecter),
m_Data(reac.m_Data)
{
m_Proxy->attach(m_ReceiveDestruct);
}
Reactive(Reactive && reac) :
m_SendDestruct(std::move(reac.m_SendDestruct)),
m_Proxy(reac.m_Proxy),
m_ReceiveDestruct(std::function<void()>(std::bind(&Reactive::Unreact, this))),
m_Affecter(reac.m_Affecter),
m_Data(reac.m_Data)
{
m_Proxy->attach(m_ReceiveDestruct);
}
Reactive & operator=(const T & data)
{
m_Data = data;
return *this;
}
Reactive & operator=(const Reactive & reac)
{
m_Proxy = reac.m_Proxy;
m_Proxy.attach(m_ReceiveDestruct);
m_Affecter = reac.m_Affecter;
m_Data = reac.m_Data;
}
Reactive & operator=(Reactive && reac)
{
m_SendDestruct = std::move(reac.m_SendDestruct);
m_Proxy = reac.m_Proxy;
m_Affecter(reac.m_Affecter);
m_Data(reac.m_Data);
}
template<class H>
Reactive & operator+(const H & rhs)
{
m_Data += rhs;
return *this;
}
template<class H>
auto operator+(Reactive<H> & rhs) -> Reactive<decltype(m_Data + rhs.m_Data)> &&
{
Reactive<decltype(m_Data + rhs.m_Data)> m_temp;
std::function<decltype(m_Data + rhs.m_Data)()> func;
if (!rhs.m_Affecter)
func = [&](){ return m_Data + rhs.m_Data;};
else
func = [&](){return m_Data + rhs.m_Affecter();};
m_SendDestruct->connect((*m_temp.m_Proxy));
rhs.m_SendDestruct->connect((*m_temp.m_Proxy));
return std::forward<Reactive<decltype(m_Data+rhs.m_Data)> &&>(m_temp);
}
template<class H>
Reactive && operator+(Reactive<H> && rhs)
{
Reactive && m_Temp
}
T & Get()
{
return m_Data;
}
void Update()
{
if(m_Affecter)
m_Data = m_Affecter();
}
void Unreact()
{
m_Affecter = nullptr;
(*m_SendDestruct)();
}
private:
std::unique_ptr<boost::signals2::signal<void()> > m_SendDestruct;
std::shared_ptr<Proxy> m_Proxy;
Slot m_ReceiveDestruct;
std::function<T()> m_Affecter;
T m_Data;
};
和简单的测试
int main()
{
Reactive<int> vel(10);
Reactive<int> acc(5);
Reactive<int> time(5);
Reactive<int> result = vel + acc + time;
system("PAUSE");
return 0;
}
以下是警告/错误:
1>main.cpp(86): warning C4355: 'this' : used in base member initializer list
1> main.cpp(83) : while compiling class template member function 'Reactive<T>::Reactive(Reactive<T> &&)'
1> with
1> [
1> T=int
1> ]
1> main.cpp(174) : see reference to class template instantiation 'Reactive<T>' being compiled
1> with
1> [
1> T=int
1> ]
1>main.cpp(66): warning C4355: 'this' : used in base member initializer list
1> main.cpp(174) : see reference to function template instantiation 'Reactive<T>::Reactive<int>(const H &)' being compiled
1> with
1> [
1> T=int,
1> H=int
1> ]
1>main.cpp(56): warning C4355: 'this' : used in base member initializer list
1> main.cpp(53) : while compiling class template member function 'Reactive<T>::Reactive(void)'
1> with
1> [
1> T=int
1> ]
1>c:\program files (x86)\boost\boost_1_53_0\boost\signals2\detail\slot_template.hpp(156): error C2248: 'Reactive<T>::Proxy::Proxy' : cannot access private member declared in class 'Reactive<T>::Proxy'
1> with
1> [
1> T=int
1> ]
1> main.cpp(32) : see declaration of 'Reactive<T>::Proxy::Proxy'
1> with
1> [
1> T=int
1> ]
1> main.cpp(30) : see declaration of 'Reactive<T>::Proxy'
1> with
1> [
1> T=int
1> ]
1> c:\program files (x86)\boost\boost_1_53_0\boost\signals2\detail\slot_template.hpp(81) : see reference to function template instantiation 'void boost::signals2::slot0<R,SlotFunction>::init_slot_function<F>(const F &)' being compiled
1> with
1> [
1> R=void,
1> SlotFunction=boost::function<void (void)>,
1> F=Reactive<int>::Proxy
1> ]
1> main.cpp(135) : see reference to function template instantiation 'boost::signals2::slot0<R,SlotFunction>::slot0<Reactive<T>::Proxy>(const F &)' being compiled
1> with
1> [
1> R=void,
1> SlotFunction=boost::function<void (void)>,
1> T=int,
1> F=Reactive<int>::Proxy
1> ]
1> main.cpp(178) : see reference to function template instantiation 'Reactive<T> &&Reactive<T>::operator +<int>(Reactive<T> &)' being compiled
1> with
1> [
1> T=int
1> ]
1>
1>Build FAILED.
1>
1>Time Elapsed 00:00:04.20
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========