From 6b1d213cc2addb10d32be1b11a66eaa622b24041 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 8 Nov 2014 09:40:17 -0800 Subject: [PATCH] Add weak_fn --- Builds/VisualStudio2013/RippleD.vcxproj | 5 + .../VisualStudio2013/RippleD.vcxproj.filters | 6 + src/beast/beast/utility/Utility.unity.cpp | 1 + .../beast/utility/tests/weak_fn.test.cpp | 146 +++++++++++++++ src/beast/beast/weak_fn.h | 175 ++++++++++++++++++ 5 files changed, 333 insertions(+) create mode 100644 src/beast/beast/utility/tests/weak_fn.test.cpp create mode 100644 src/beast/beast/weak_fn.h diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index e8e69db1f..75b4e225c 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -987,6 +987,9 @@ True + + True + True @@ -999,6 +1002,8 @@ + + True diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index 7224346d2..d5af87b59 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -1674,6 +1674,9 @@ beast\utility\tests + + beast\utility\tests + beast\utility\tests @@ -1689,6 +1692,9 @@ beast + + beast + hyperleveldb\db diff --git a/src/beast/beast/utility/Utility.unity.cpp b/src/beast/beast/utility/Utility.unity.cpp index d33f23ee4..71ce60e47 100644 --- a/src/beast/beast/utility/Utility.unity.cpp +++ b/src/beast/beast/utility/Utility.unity.cpp @@ -33,4 +33,5 @@ #include #include #include +#include #include diff --git a/src/beast/beast/utility/tests/weak_fn.test.cpp b/src/beast/beast/utility/tests/weak_fn.test.cpp new file mode 100644 index 000000000..7137fc978 --- /dev/null +++ b/src/beast/beast/utility/tests/weak_fn.test.cpp @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2013, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include + +namespace beast { + +class weak_fn_test : public beast::unit_test::suite +{ +public: + struct T + { + bool& called_; + + explicit + T (bool& called) + : called_(called) + { + } + + void + fv() + { + called_ = true; + } + + void + fi(int i) + { + called_ = true; + } + + void + fis(int, std::string) + { + called_ = true; + } + + int + fri() + { + called_ = true; + return 2; + } + }; + + void + run() + { + { + bool called = false; + auto const p = std::make_shared(called); + std::bind(weak_fn(&T::fv, p))(); + expect(called); + } + + { + bool called = false; + auto p = std::make_shared(called); + auto call = std::bind(weak_fn(&T::fv, p)); + p.reset(); + call(); + expect(! called); + } + + { + bool called = false; + auto p = std::make_shared(called); + std::bind(weak_fn(&T::fi, p), 1)(); + expect(called); + } + + { + bool called = false; + auto p = std::make_shared(called); + std::bind(weak_fn(&T::fi, p), + std::placeholders::_1)(1); + expect(called); + } + + { + bool called = false; + auto p = std::make_shared(called); + std::bind(weak_fn(&T::fis, p), + 1, std::placeholders::_1)("foo"); + expect(called); + } + + { + bool called = false; + auto p = std::make_shared(called); + try + { + std::bind(weak_fn(&T::fis, p, throw_if_invalid<>()), + 1, std::placeholders::_1)("foo"); + p.reset(); + fail(); + } + catch(std::bad_weak_ptr const&) + { + expect(! called); + } + } + + { + bool called = false; + auto p = std::make_shared(called); + expect(std::bind(weak_fn(&T::fri, p))() == 2); + expect(called); + } + + { + bool called = false; + auto p = std::make_shared(called); + auto call = std::bind(weak_fn(&T::fv, p, + [&called]() + { + called = true; + })); + p.reset(); + call(); + expect(called); + } + } +}; + +BEAST_DEFINE_TESTSUITE(weak_fn,asio,beast); + +} diff --git a/src/beast/beast/weak_fn.h b/src/beast/beast/weak_fn.h new file mode 100644 index 000000000..67eb335d0 --- /dev/null +++ b/src/beast/beast/weak_fn.h @@ -0,0 +1,175 @@ +//------------------------------------------------------------------------------ +/* + This file is part of Beast: https://github.com/vinniefalco/Beast + Copyright 2014, Vinnie Falco + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef BEAST_WEAK_FN_H_INCLUDED +#define BEAST_WEAK_FN_H_INCLUDED + +#include +#include + +// Original version: +// http://lists.boost.org/Archives/boost/att-189469/weak_fn.hpp +// +// This work was adapted from source code with this copyright notice: +// +// weak_fun.hpp +// +// Copyright (c) 2009 Artyom Beilis +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +namespace beast { + +// Policy throws if weak pointer is expired +template +struct throw_if_invalid +{ + V operator()() const + { + throw std::bad_weak_ptr(); + } +}; + +// Policy returns a value if weak pointer is expired +template +struct return_default_if_invalid +{ + return_default_if_invalid() + : def_value_() + { } + + return_default_if_invalid(V def_value) + : def_value_(def_value) + { } + + V operator()() const + { + return def_value_; + } + +private: + V def_value_; +}; + +// Policy does nothing if weak pointer is expired +template +struct ignore_if_invalid +{ + V operator()() const + { + return V(); + } +}; + +template +using default_invalid_policy = ignore_if_invalid; + +namespace detail { + +template +class weak_binder + : private beast::empty_base_optimization +{ +private: + typedef R (T::*member_type)(Args...); + using pointer_type = std::weak_ptr; + using shared_type = std::shared_ptr; + member_type member_; + pointer_type object_; + +public: + using result_type = R; + + weak_binder (member_type member, + Policy policy, pointer_type object) + : empty_base_optimization(std::move(policy)) + , member_(member) + , object_(object) + { } + + R operator()(Args... args) + { + if(auto p = object_.lock()) + return ((*p).*member_)(args...); + return this->member()(); + } +}; + +} // detail + +/** Returns a callback that can be used with std::bind and a weak_ptr. + When called, it tries to lock weak_ptr to get a shared_ptr. If successful, + it calls given member function with given arguments. If not successful, + the policy functor is called. Built-in policies are: + + ignore_if_invalid does nothing + throw_if_invalid throws `bad_weak_ptr` + return_default_if_invalid returns a chosen value + + Example: + + struct Foo { + void bar(int i) { + std::cout << i << std::endl; + } + }; + + struct do_something { + void operator()() { + std::cout << "outdated reference" << std::endl; + } + }; + + int main() + { + std::shared_ptr sp(new Foo()); + std::weak_ptr wp(sp); + + std::bind(weak_fn(&Foo::bar, wp), _1)(1); + sp.reset(); + std::bind(weak_fn(&Foo::bar, wp), 1)(); + std::bind(weak_fn(&Foo::bar, wp, do_something()), 1)(); + } +*/ +/** @{ */ +template +detail::weak_binder +weak_fn (R (T::*member)(Args...), std::shared_ptr p, + Policy policy) +{ + return detail::weak_binder(member, policy, p); +} + +template +detail::weak_binder, Args...> +weak_fn (R (T::*member)(Args...), std::shared_ptr p) +{ + return detail::weak_binder, Args...>(member, + default_invalid_policy{}, p); +} +/** @} */ + +} // beast + +#endif