diff --git a/fluent++/CallbackContainer.h b/fluent++/CallbackContainer.h index 38b64a0..9c78dda 100644 --- a/fluent++/CallbackContainer.h +++ b/fluent++/CallbackContainer.h @@ -20,19 +20,20 @@ #include +#include +#include + #include "Callback.h" class CallbackContainer { std::shared_ptr self_reference; + typedef size_t handle_t; + size_t handle; - struct InternalReference - { - }; - - std::unordered_map container; + std::unordered_map container; template struct ProxyFactory; @@ -40,14 +41,24 @@ class CallbackContainer template struct ProxyFactory<_CTy, std::tuple> { - // Creates a weak callback proxy which prevents invoking to an invalid context. - static callback_of_t<_CTy> CreateProxy(_CTy&& callback) + // Creates a weak proxy callback which prevents invoking to an invalid context. + static callback_of_t<_CTy> CreateProxy(std::weak_ptr const& weak_owner, + size_t const handle, weak_callback_of_t<_CTy> const& weak_callback) { - return [callback](Args&&... args) + return [=](Args&&... args) { + // Try to get a pointer to the owner + if (auto const owner = weak_owner.lock()) + // And to the wrapped functional itself + if (auto const callback = weak_callback.lock()) + { + // Invoke the original callback + // FIXME: std::forward(args...) causes errors when void + (*callback)(args...); - // Invoke the original callback - callback(std::forward(args...)); + // Unregister the callback + owner->InvalidateCallback(handle); + } }; } }; @@ -67,7 +78,6 @@ public: CallbackContainer& Clear() { container.clear(); - handle = 0L; return *this; } @@ -75,13 +85,32 @@ public: auto operator()(_CTy&& callback) -> callback_of_t<_CTy> { + // Create the shared callback + shared_callback_of_t<_CTy> shared_callback = make_shared_callback(std::forward<_CTy>(callback)); + // Create a weak proxy callback which removes the callback on execute + auto const this_handle = handle++; callback_of_t<_CTy> proxy = ProxyFactory<_CTy, ::fu::argument_type_of_t<_CTy>>:: - CreateProxy(std::forward<_CTy>(callback)); + CreateProxy(self_reference, this_handle, shared_callback); + container.insert(std::make_pair(this_handle, boost::any(std::move(shared_callback)))); return std::move(proxy); } + + boost::optional GetLastCallbackHandle() const + { + if (handle == 0L) + return boost::none; + else + return boost::make_optional(handle); + } + + CallbackContainer& InvalidateCallback(handle_t const handle) + { + container.erase(handle); + return *this; + } }; #endif /// _CALLBACK_CONTAINER_H_ diff --git a/test.cpp b/test.cpp index 1662363..e984b9c 100644 --- a/test.cpp +++ b/test.cpp @@ -4,6 +4,9 @@ #include "Callback.h" #include "CallbackContainer.h" +#include +#include + void CastSpell(int id, Callback const& callback) { std::cout << "Cast " << id << std::endl; @@ -47,12 +50,47 @@ int main(int argc, char** argv) }); - CallbackContainer callback; + Callback<> weak_cb_test; - auto mycb = callback([](bool success) { + CallbackContainer callback; - }); + std::shared_ptr dealloc_test(new int{2}, [](int* me) + { + std::cout << "dealloc ok" << std::endl; + delete me; + }); + + auto const cb_void = callback([dealloc_test] + { + std::cout << "huhu i'm a..." << std::endl; + + auto const cp = dealloc_test; + }); + + dealloc_test.reset(); + + auto const cb_bool = callback([](bool success) + { + std::cout << "...weak wrapped callback!" << std::endl; + }); + + weak_cb_test = callback([] + { + std::cout << "huhu i'm crashsafe (you wont see me)!" << std::endl; + std::logic_error("bad logic"); + }); + + cb_void(); + + int i = 0; + ++i; + + cb_bool(true); + } + + // This will never be executed because the CallbackContainer was deallocated and its weak callback proxies are crash safe. + weak_cb_test(); return 0; }