From d39f6806a2b39e09dc7320cbfe63b8a998a3ae55 Mon Sep 17 00:00:00 2001 From: Rui Mo Date: Tue, 2 Feb 2021 09:09:23 +0000 Subject: [PATCH] use optimized std function --- .../codegen/arrow_compute/ext/cmp_function.h | 7 +- .../codegen/arrow_compute/ext/sort_kernel.cc | 2 +- cpp/src/tests/arrow_compute_test_sort.cc | 67 +- cpp/src/third_party/function.h | 630 ++++++++++++++++++ 4 files changed, 675 insertions(+), 31 deletions(-) create mode 100644 cpp/src/third_party/function.h diff --git a/cpp/src/codegen/arrow_compute/ext/cmp_function.h b/cpp/src/codegen/arrow_compute/ext/cmp_function.h index 7ea63891d..2194e3c70 100644 --- a/cpp/src/codegen/arrow_compute/ext/cmp_function.h +++ b/cpp/src/codegen/arrow_compute/ext/cmp_function.h @@ -19,6 +19,7 @@ #include #include +#include "third_party/function.h" namespace sparkcolumnarplugin { namespace codegen { @@ -32,7 +33,7 @@ class TypedComparator { ~TypedComparator() {} - std::function GetCompareFunc( + func::function GetCompareFunc( const arrow::ArrayVector& arrays, bool asc, bool nulls_first) { uint64_t null_total = 0; std::vector> typed_arrays; @@ -161,7 +162,7 @@ class StringComparator { ~StringComparator() {} - std::function GetCompareFunc( + func::function GetCompareFunc( const arrow::ArrayVector& arrays, bool asc, bool nulls_first) { uint64_t null_total = 0; std::vector> typed_arrays; @@ -303,7 +304,7 @@ static arrow::Status MakeCmpFunction( std::vector key_index_list, std::vector sort_directions, std::vector nulls_order, - std::vector>& cmp_functions) { + std::vector>& cmp_functions) { for (int i = 0; i < key_field_list.size(); i++) { auto type = key_field_list[i]->type(); int key_col_id = key_index_list[i]; diff --git a/cpp/src/codegen/arrow_compute/ext/sort_kernel.cc b/cpp/src/codegen/arrow_compute/ext/sort_kernel.cc index ffc193908..9520826d2 100644 --- a/cpp/src/codegen/arrow_compute/ext/sort_kernel.cc +++ b/cpp/src/codegen/arrow_compute/ext/sort_kernel.cc @@ -1760,7 +1760,7 @@ class SortMultiplekeyKernel : public SortArraysToIndicesKernel::Impl { uint64_t num_batches_ = 0; uint64_t items_total_ = 0; int col_num_; - std::vector> cmp_functions_; + std::vector> cmp_functions_; class SorterResultIterator : public ResultIterator { public: diff --git a/cpp/src/tests/arrow_compute_test_sort.cc b/cpp/src/tests/arrow_compute_test_sort.cc index 98a6ce6df..cc805f9dd 100644 --- a/cpp/src/tests/arrow_compute_test_sort.cc +++ b/cpp/src/tests/arrow_compute_test_sort.cc @@ -64,12 +64,8 @@ TEST(TestArrowComputeSort, SortTestInPlaceNullsFirstAsc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; -<<<<<<< HEAD - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); -======= - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, - &sort_expr, true)); ->>>>>>> 930de59a... support mul-key sort without projection + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -156,7 +152,8 @@ TEST(TestArrowComputeSort, SortTestInplaceNullsLastAsc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -243,7 +240,8 @@ TEST(TestArrowComputeSort, SortTestInplaceNullsFirstDesc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -330,7 +328,8 @@ TEST(TestArrowComputeSort, SortTestInplaceNullsLastDesc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -417,7 +416,8 @@ TEST(TestArrowComputeSort, SortTestInplaceAsc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -504,7 +504,8 @@ TEST(TestArrowComputeSort, SortTestInplaceDesc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -590,7 +591,8 @@ TEST(TestArrowComputeSort, SortTestOnekeyNullsFirstAsc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; std::vector> dummy_result_batches; @@ -683,7 +685,8 @@ TEST(TestArrowComputeSort, SortTestOnekeyNullsLastAsc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -764,7 +767,8 @@ TEST(TestArrowComputeSort, SortTestOnekeyNullsFirstDesc) { auto do_codegen = TreeExprBuilder::MakeFunction( "codegen", {TreeExprBuilder::MakeLiteral(false)}, uint32()); auto n_sort_to_indices = TreeExprBuilder::MakeFunction( - "sortArraysToIndices", {n_key_func, n_key_field, n_dir, n_nulls_order, NaN_check, do_codegen}, uint32()); + "sortArraysToIndices", + {n_key_func, n_key_field, n_dir, n_nulls_order, NaN_check, do_codegen}, uint32()); auto n_sort = TreeExprBuilder::MakeFunction( "standalone", {n_sort_to_indices}, uint32()); auto sortArrays_expr = TreeExprBuilder::MakeExpression(n_sort, f_res); @@ -774,7 +778,8 @@ TEST(TestArrowComputeSort, SortTestOnekeyNullsFirstDesc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -866,7 +871,8 @@ TEST(TestArrowComputeSort, SortTestOnekeyNullsLastDesc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -961,7 +967,8 @@ TEST(TestArrowComputeSort, SortTestOnekeyBooleanDesc) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -1058,7 +1065,8 @@ TEST(TestArrowComputeSort, SortTestOneKeyStr) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; std::vector> dummy_result_batches; @@ -1148,7 +1156,8 @@ TEST(TestArrowComputeSort, SortTestOneKeyWithProjection) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; std::vector> dummy_result_batches; @@ -1238,7 +1247,8 @@ TEST(TestArrowComputeSort, SortTestMultipleKeysNaN) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -1351,7 +1361,8 @@ TEST(TestArrowComputeSort, SortTestMultipleKeysWithProjection) { "isnull", {arg_2}, arrow::boolean()); auto n_key_func = TreeExprBuilder::MakeFunction( - "key_function", {coalesce_0, isnull_0, coalesce_1, isnull_1, coalesce_2, isnull_2}, uint32()); + "key_function", + {coalesce_0, isnull_0, coalesce_1, isnull_1, coalesce_2, isnull_2}, uint32()); auto n_key_field = TreeExprBuilder::MakeFunction( "key_field", {arg_0, arg_0, arg_1, arg_1, arg_2, arg_2}, uint32()); auto n_dir = TreeExprBuilder::MakeFunction( @@ -1377,7 +1388,8 @@ TEST(TestArrowComputeSort, SortTestMultipleKeysWithProjection) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK(CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -1488,8 +1500,8 @@ TEST(TestArrowComputeSort, SortTestMultipleKeysWithoutCodegen) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK( - CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; @@ -1602,7 +1614,8 @@ TEST(TestArrowComputeSort, SortTestMultipleKeysWithoutCodegenWithProjection) { "isnull", {arg_2}, arrow::boolean()); auto n_key_func = TreeExprBuilder::MakeFunction( - "key_function", {coalesce_0, isnull_0, coalesce_1, isnull_1, coalesce_2, isnull_2}, uint32()); + "key_function", + {coalesce_0, isnull_0, coalesce_1, isnull_1, coalesce_2, isnull_2}, uint32()); auto n_key_field = TreeExprBuilder::MakeFunction( "key_field", {arg_0, arg_0, arg_1, arg_1, arg_2, arg_2}, uint32()); auto n_dir = TreeExprBuilder::MakeFunction( @@ -1628,8 +1641,8 @@ TEST(TestArrowComputeSort, SortTestMultipleKeysWithoutCodegenWithProjection) { ///////////////////// Calculation ////////////////// std::shared_ptr sort_expr; arrow::compute::FunctionContext ctx; - ASSERT_NOT_OK( - CreateCodeGenerator(ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); + ASSERT_NOT_OK(CreateCodeGenerator( + ctx.memory_pool(), sch, {sortArrays_expr}, ret_types, &sort_expr, true)); std::shared_ptr input_batch; std::vector> input_batch_list; diff --git a/cpp/src/third_party/function.h b/cpp/src/third_party/function.h new file mode 100644 index 000000000..291d46ab3 --- /dev/null +++ b/cpp/src/third_party/function.h @@ -0,0 +1,630 @@ +/* +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