MostlyHarmless 0.0.1
 
Loading...
Searching...
No Matches
mostly_harmless::utils::Proxy< T > Class Template Referencefinal

Helper class for managing lifetimes of captured references in a lambda. More...

#include <mostlyharmless_Proxy.h>

Public Member Functions

 Proxy (Private, T *toWrap)
 
T * getWrapped () noexcept
 
void null () noexcept
 
bool isValid () noexcept
 

Static Public Member Functions

static std::shared_ptr< Proxy< T > > create (T *toWrap) noexcept
 

Detailed Description

template<typename T>
class mostly_harmless::utils::Proxy< T >

Helper class for managing lifetimes of captured references in a lambda.

Take a scenario where you have a timer thread, which relies on some member variable being valid - for example,

class Something final {
public:
Something() {
auto timerCallback = [this]() -> void {
++m_callCount;
};
m_timer.action = std::move(timerCallback);
m_timer.run(100);
}
private:
std::atomic<int> m_callCount{ 0 };
};
Definition mostlyharmless_Timer.h:10

Now, lets say between calls to our timer action, our instance of Something gets destroyed - but the timer callback is still running. In this case, we have a segfault, as this == nullptr. This comes up fairly often with timers, and is a massive PITA.

The use case for Proxy then, is to be able to reliably check if the captured variables are still valid. This works by constructing a shared_ptr to some "Proxy" class with a pointer to the data you want to check, and ensuring its lifecycle matches that of the wrapped pointer. When your wrapped object is destroyed, make sure to call null() on the proxy, to invalidate its pointer member. You can then capture the shared_ptr via copy to your timer callback, which will ensure that while the generated closure for the lambda is alive, so is our proxy. Finally, in your callback, you can simply call theProxyInstance.isValid() to check whether the wrapped pointer is still valid, and bail if it isn't. Putting that together for our earlier example then:

class Something final {
public:
Something() {
auto proxyCopy = m_proxy;
auto timerCallback = [this, proxyCopy]() -> void {
if(!proxyCopy->isValid()) {
return;
}
++m_callCount;
};
m_timer.action = std::move(timerCallback);
m_timer.run(100);
}
~Something() {
m_proxy->null();
}
private:
std::shared_ptr<mostly_harmless::utils::Proxy<Something>> m_proxy{ nullptr };
std::atomic<int> m_callCount{ 0 };
mostly_harmless::utils::Timer m_timer;
};
static std::shared_ptr< Proxy< T > > create(T *toWrap) noexcept
Definition mostlyharmless_Proxy.h:86

Constructor & Destructor Documentation

◆ Proxy()

template<typename T>
mostly_harmless::utils::Proxy< T >::Proxy ( Private ,
T * toWrap )
inlineexplicit

Member Function Documentation

◆ create()

template<typename T>
static std::shared_ptr< Proxy< T > > mostly_harmless::utils::Proxy< T >::create ( T * toWrap)
inlinestaticnodiscardnoexcept

Create a std::shared_ptr<Proxy<T>>, wrapping whatever you pass to toWrap.

Parameters
toWrapa raw pointer to the data you want to wrap.

◆ getWrapped()

template<typename T>
T * mostly_harmless::utils::Proxy< T >::getWrapped ( )
inlinenodiscardnoexcept

Retrieve the pointer this object is wrapping.

Returns
The wrapped pointer. Make sure to check this for null.

◆ isValid()

template<typename T>
bool mostly_harmless::utils::Proxy< T >::isValid ( )
inlinenodiscardnoexcept

Checks whether the wrapped data is still allocated.

Returns
true if the data is valid, false otherwise.

◆ null()

template<typename T>
void mostly_harmless::utils::Proxy< T >::null ( )
inlinenoexcept

nulls the wrapped pointer - make SURE you call this when whatever data your proxy is wrapping gets destroyed!


The documentation for this class was generated from the following file: