/* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ // despite that it would be nice if you give credit to Malte Skarupke #pragma once #include #include #include #include #include #include #ifdef _MSC_VER #define FUNC_NOEXCEPT #define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR) #define FUNC_CONSTEXPR const #else #define FUNC_NOEXCEPT noexcept #define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR) noexcept(detail::is_inplace_allocated::value) #define FUNC_CONSTEXPR constexpr #endif #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif #define FUNC_MOVE(value) static_cast::type &&>(value) #define FUNC_FORWARD(type, value) static_cast(value) namespace func { #ifndef FUNC_NO_EXCEPTIONS struct bad_function_call : std::exception { const char * what() const FUNC_NOEXCEPT override { return "Bad function call"; } }; #endif template struct force_function_heap_allocation : std::false_type { }; template class function; namespace detail { struct manager_storage_type; struct function_manager; struct functor_padding { protected: size_t padding_first; size_t padding_second; }; struct empty_struct { }; # ifndef FUNC_NO_EXCEPTIONS template Result empty_call(const functor_padding &, Arguments...) { throw bad_function_call(); } # endif template struct is_inplace_allocated { static const bool value // so that it fits = sizeof(T) <= sizeof(functor_padding) // so that it will be aligned && std::alignment_of::value % std::alignment_of::value == 0 // so that we can offer noexcept move && std::is_nothrow_move_constructible::value // so that the user can override it && !force_function_heap_allocation::value; }; template T to_functor(T && func) { return FUNC_FORWARD(T, func); } template auto to_functor(Result (Class::*func)(Arguments...)) -> decltype(std::mem_fn(func)) { return std::mem_fn(func); } template auto to_functor(Result (Class::*func)(Arguments...) const) -> decltype(std::mem_fn(func)) { return std::mem_fn(func); } template struct functor_type { typedef decltype(to_functor(std::declval())) type; }; template bool is_null(const T &) { return false; } template bool is_null(Result (* const & function_pointer)(Arguments...)) { return function_pointer == nullptr; } template bool is_null(Result (Class::* const & function_pointer)(Arguments...)) { return function_pointer == nullptr; } template bool is_null(Result (Class::* const & function_pointer)(Arguments...) const) { return function_pointer == nullptr; } template struct is_valid_function_argument { static const bool value = false; }; template struct is_valid_function_argument, Result (Arguments...)> { static const bool value = false; }; template struct is_valid_function_argument { # ifdef _MSC_VER // as of january 2013 visual studio doesn't support the SFINAE below static const bool value = true; # else template static decltype(to_functor(std::declval())(std::declval()...)) check(U *); template static empty_struct check(...); static const bool value = std::is_convertible(nullptr)), Result>::value; # endif }; typedef const function_manager * manager_type; struct manager_storage_type { template Allocator & get_allocator() FUNC_NOEXCEPT { return reinterpret_cast(manager); } template const Allocator & get_allocator() const FUNC_NOEXCEPT { return reinterpret_cast(manager); } functor_padding functor; manager_type manager; }; template struct function_manager_inplace_specialization { template static Result call(const functor_padding & storage, Arguments... arguments) { // do not call get_functor_ref because I want this function to be fast // in debug when nothing gets inlined return const_cast(reinterpret_cast(storage))(FUNC_FORWARD(Arguments, arguments)...); } static void store_functor(manager_storage_type & storage, T to_store) { new (&get_functor_ref(storage)) T(FUNC_FORWARD(T, to_store)); } static void move_functor(manager_storage_type & lhs, manager_storage_type && rhs) FUNC_NOEXCEPT { new (&get_functor_ref(lhs)) T(FUNC_MOVE(get_functor_ref(rhs))); } static void destroy_functor(Allocator &, manager_storage_type & storage) FUNC_NOEXCEPT { get_functor_ref(storage).~T(); } static T & get_functor_ref(const manager_storage_type & storage) FUNC_NOEXCEPT { return const_cast(reinterpret_cast(storage.functor)); } }; template struct function_manager_inplace_specialization::value>::type> { template static Result call(const functor_padding & storage, Arguments... arguments) { // do not call get_functor_ptr_ref because I want this function to be fast // in debug when nothing gets inlined return (*reinterpret_cast::pointer &>(storage))(FUNC_FORWARD(Arguments, arguments)...); } static void store_functor(manager_storage_type & self, T to_store) { Allocator & allocator = self.get_allocator();; static_assert(sizeof(typename std::allocator_traits::pointer) <= sizeof(self.functor), "The allocator's pointer type is too big"); typename std::allocator_traits::pointer * ptr = new (&get_functor_ptr_ref(self)) typename std::allocator_traits::pointer(std::allocator_traits::allocate(allocator, 1)); std::allocator_traits::construct(allocator, *ptr, FUNC_FORWARD(T, to_store)); } static void move_functor(manager_storage_type & lhs, manager_storage_type && rhs) FUNC_NOEXCEPT { static_assert(std::is_nothrow_move_constructible::pointer>::value, "we can't offer a noexcept swap if the pointer type is not nothrow move constructible"); new (&get_functor_ptr_ref(lhs)) typename std::allocator_traits::pointer(FUNC_MOVE(get_functor_ptr_ref(rhs))); // this next assignment makes the destroy function easier get_functor_ptr_ref(rhs) = nullptr; } static void destroy_functor(Allocator & allocator, manager_storage_type & storage) FUNC_NOEXCEPT { typename std::allocator_traits::pointer & pointer = get_functor_ptr_ref(storage); if (!pointer) return; std::allocator_traits::destroy(allocator, pointer); std::allocator_traits::deallocate(allocator, pointer, 1); } static T & get_functor_ref(const manager_storage_type & storage) FUNC_NOEXCEPT { return *get_functor_ptr_ref(storage); } static typename std::allocator_traits::pointer & get_functor_ptr_ref(manager_storage_type & storage) FUNC_NOEXCEPT { return reinterpret_cast::pointer &>(storage.functor); } static const typename std::allocator_traits::pointer & get_functor_ptr_ref(const manager_storage_type & storage) FUNC_NOEXCEPT { return reinterpret_cast::pointer &>(storage.functor); } }; template static const function_manager & get_default_manager(); template static void create_manager(manager_storage_type & storage, Allocator && allocator) { new (&storage.get_allocator()) Allocator(FUNC_MOVE(allocator)); storage.manager = &get_default_manager(); } // this struct acts as a vtable. it is an optimization to prevent // code-bloat from rtti. see the documentation of boost::function struct function_manager { template inline static FUNC_CONSTEXPR function_manager create_default_manager() { # ifdef _MSC_VER function_manager result = # else return function_manager # endif { &templated_call_move_and_destroy, &templated_call_copy, &templated_call_copy_functor_only, &templated_call_destroy, # ifndef FUNC_NO_RTTI &templated_call_type_id, &templated_call_target # endif }; # ifdef _MSC_VER return result; # endif } void (* const call_move_and_destroy)(manager_storage_type & lhs, manager_storage_type && rhs); void (* const call_copy)(manager_storage_type & lhs, const manager_storage_type & rhs); void (* const call_copy_functor_only)(manager_storage_type & lhs, const manager_storage_type & rhs); void (* const call_destroy)(manager_storage_type & manager); # ifndef FUNC_NO_RTTI const std::type_info & (* const call_type_id)(); void * (* const call_target)(const manager_storage_type & manager, const std::type_info & type); # endif template static void templated_call_move_and_destroy(manager_storage_type & lhs, manager_storage_type && rhs) { typedef function_manager_inplace_specialization specialization; specialization::move_functor(lhs, FUNC_MOVE(rhs)); specialization::destroy_functor(rhs.get_allocator(), rhs); create_manager(lhs, FUNC_MOVE(rhs.get_allocator())); rhs.get_allocator().~Allocator(); } template static void templated_call_copy(manager_storage_type & lhs, const manager_storage_type & rhs) { typedef function_manager_inplace_specialization specialization; create_manager(lhs, Allocator(rhs.get_allocator())); specialization::store_functor(lhs, specialization::get_functor_ref(rhs)); } template static void templated_call_destroy(manager_storage_type & self) { typedef function_manager_inplace_specialization specialization; specialization::destroy_functor(self.get_allocator(), self); self.get_allocator().~Allocator(); } template static void templated_call_copy_functor_only(manager_storage_type & lhs, const manager_storage_type & rhs) { typedef function_manager_inplace_specialization specialization; specialization::store_functor(lhs, specialization::get_functor_ref(rhs)); } # ifndef FUNC_NO_RTTI template static const std::type_info & templated_call_type_id() { return typeid(T); } template static void * templated_call_target(const manager_storage_type & self, const std::type_info & type) { typedef function_manager_inplace_specialization specialization; if (type == typeid(T)) return &specialization::get_functor_ref(self); else return nullptr; } # endif }; template inline static const function_manager & get_default_manager() { static FUNC_CONSTEXPR function_manager default_manager = function_manager::create_default_manager(); return default_manager; } template struct typedeffer { typedef Result result_type; }; template struct typedeffer { typedef Result result_type; typedef Argument argument_type; }; template struct typedeffer { typedef Result result_type; typedef First_Argument first_argument_type; typedef Second_Argument second_argument_type; }; } template class function : public detail::typedeffer { public: function() FUNC_NOEXCEPT { initialize_empty(); } function(std::nullptr_t) FUNC_NOEXCEPT { initialize_empty(); } function(function && other) FUNC_NOEXCEPT { initialize_empty(); swap(other); } function(const function & other) : call(other.call) { other.manager_storage.manager->call_copy(manager_storage, other.manager_storage); } template function(T functor, typename std::enable_if::value, detail::empty_struct>::type = detail::empty_struct()) FUNC_TEMPLATE_NOEXCEPT(T, std::allocator::type>) { if (detail::is_null(functor)) { initialize_empty(); } else { typedef typename detail::functor_type::type functor_type; initialize(detail::to_functor(FUNC_FORWARD(T, functor)), std::allocator()); } } template function(std::allocator_arg_t, const Allocator &) { // ignore the allocator because I don't allocate initialize_empty(); } template function(std::allocator_arg_t, const Allocator &, std::nullptr_t) { // ignore the allocator because I don't allocate initialize_empty(); } template function(std::allocator_arg_t, const Allocator & allocator, T functor, typename std::enable_if::value, detail::empty_struct>::type = detail::empty_struct()) FUNC_TEMPLATE_NOEXCEPT(T, Allocator) { if (detail::is_null(functor)) { initialize_empty(); } else { initialize(detail::to_functor(FUNC_FORWARD(T, functor)), Allocator(allocator)); } } template function(std::allocator_arg_t, const Allocator & allocator, const function & other) : call(other.call) { typedef typename std::allocator_traits::template rebind_alloc MyAllocator; // first try to see if the allocator matches the target type detail::manager_type manager_for_allocator = &detail::get_default_manager::value_type, Allocator>(); if (other.manager_storage.manager == manager_for_allocator) { detail::create_manager::value_type, Allocator>(manager_storage, Allocator(allocator)); manager_for_allocator->call_copy_functor_only(manager_storage, other.manager_storage); } // if it does not, try to see if the target contains my type. this // breaks the recursion of the last case. otherwise repeated copies // would allocate more and more memory else { detail::manager_type manager_for_function = &detail::get_default_manager(); if (other.manager_storage.manager == manager_for_function) { detail::create_manager(manager_storage, MyAllocator(allocator)); manager_for_function->call_copy_functor_only(manager_storage, other.manager_storage); } else { // else store the other function as my target initialize(other, MyAllocator(allocator)); } } } template function(std::allocator_arg_t, const Allocator &, function && other) FUNC_NOEXCEPT { // ignore the allocator because I don't allocate initialize_empty(); swap(other); } function & operator=(function other) FUNC_NOEXCEPT { swap(other); return *this; } ~function() FUNC_NOEXCEPT { manager_storage.manager->call_destroy(manager_storage); } Result operator()(Arguments... arguments) const { return call(manager_storage.functor, FUNC_FORWARD(Arguments, arguments)...); } template void assign(T && functor, const Allocator & allocator) FUNC_TEMPLATE_NOEXCEPT(T, Allocator) { function(std::allocator_arg, allocator, functor).swap(*this); } void swap(function & other) FUNC_NOEXCEPT { detail::manager_storage_type temp_storage; other.manager_storage.manager->call_move_and_destroy(temp_storage, FUNC_MOVE(other.manager_storage)); manager_storage.manager->call_move_and_destroy(other.manager_storage, FUNC_MOVE(manager_storage)); temp_storage.manager->call_move_and_destroy(manager_storage, FUNC_MOVE(temp_storage)); std::swap(call, other.call); } # ifndef FUNC_NO_RTTI const std::type_info & target_type() const FUNC_NOEXCEPT { return manager_storage.manager->call_type_id(); } template T * target() FUNC_NOEXCEPT { return static_cast(manager_storage.manager->call_target(manager_storage, typeid(T))); } template const T * target() const FUNC_NOEXCEPT { return static_cast(manager_storage.manager->call_target(manager_storage, typeid(T))); } # endif operator bool() const FUNC_NOEXCEPT { # ifdef FUNC_NO_EXCEPTIONS return call != nullptr; # else return call != &detail::empty_call; # endif } private: detail::manager_storage_type manager_storage; Result (*call)(const detail::functor_padding &, Arguments...); template void initialize(T functor, Allocator && allocator) { call = &detail::function_manager_inplace_specialization::template call; detail::create_manager(manager_storage, FUNC_FORWARD(Allocator, allocator)); detail::function_manager_inplace_specialization::store_functor(manager_storage, FUNC_FORWARD(T, functor)); } typedef Result(*Empty_Function_Type)(Arguments...); void initialize_empty() FUNC_NOEXCEPT { typedef std::allocator Allocator; static_assert(detail::is_inplace_allocated::value, "The empty function should benefit from small functor optimization"); detail::create_manager(manager_storage, Allocator()); detail::function_manager_inplace_specialization::store_functor(manager_storage, nullptr); # ifdef FUNC_NO_EXCEPTIONS call = nullptr; # else call = &detail::empty_call; # endif } }; template bool operator==(std::nullptr_t, const function & rhs) FUNC_NOEXCEPT { return !rhs; } template bool operator==(const function & lhs, std::nullptr_t) FUNC_NOEXCEPT { return !lhs; } template bool operator!=(std::nullptr_t, const function & rhs) FUNC_NOEXCEPT { return rhs; } template bool operator!=(const function & lhs, std::nullptr_t) FUNC_NOEXCEPT { return lhs; } template void swap(function & lhs, function & rhs) { lhs.swap(rhs); } } // end namespace func namespace std { template struct uses_allocator, Allocator> : std::true_type { }; } #ifdef __GNUC__ #pragma GCC diagnostic pop #endif #undef FUNC_NOEXCEPT #undef FUNC_TEMPLATE_NOEXCEPT #undef FUNC_FORWARD #undef FUNC_MOVE #undef FUNC_CONSTEXPR