From 6cb02e9619fe3dae2e71751e8d7e88fd959104eb Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 13 Jan 2016 06:44:46 +0000 Subject: [PATCH 01/14] Initial commit of working functional Functional is built using reference-counted, pool-allocated functors. Function is the reference type for functors. --- core-util/v2/detail/allocators.hpp | 48 +++++++++++ core-util/v2/detail/forward.hpp | 35 ++++++++ core-util/v2/detail/functor.hpp | 54 ++++++++++++ core-util/v2/detail/interface.hpp | 57 +++++++++++++ core-util/v2/detail/member.hpp | 64 ++++++++++++++ core-util/v2/detail/static.hpp | 49 +++++++++++ core-util/v2/functional.hpp | 131 +++++++++++++++++++++++++++++ source/v2/functional.cpp | 78 +++++++++++++++++ test/Functional/main.cpp | 34 ++++++++ 9 files changed, 550 insertions(+) create mode 100644 core-util/v2/detail/allocators.hpp create mode 100644 core-util/v2/detail/forward.hpp create mode 100644 core-util/v2/detail/functor.hpp create mode 100644 core-util/v2/detail/interface.hpp create mode 100644 core-util/v2/detail/member.hpp create mode 100644 core-util/v2/detail/static.hpp create mode 100644 core-util/v2/functional.hpp create mode 100644 source/v2/functional.cpp create mode 100644 test/Functional/main.cpp diff --git a/core-util/v2/detail/allocators.hpp b/core-util/v2/detail/allocators.hpp new file mode 100644 index 0000000..d64d0e3 --- /dev/null +++ b/core-util/v2/detail/allocators.hpp @@ -0,0 +1,48 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_DETAIL_ALLOCATORS_HPP +#define FUNCTIONAL_DETAIL_ALLOCATORS_HPP + +#include "interface.hpp" +#include "core-util/ExtendablePoolAllocator.h" +#include "ualloc/ualloc.h" + +namespace functional { +namespace detail { + +class ContainerAllocator : public mbed::util::ExtendablePoolAllocator { +public: + ContainerAllocator(size_t initial_elements, size_t new_pool_elements, size_t element_size, + UAllocTraits_t alloc_traits, unsigned alignment = MBED_UTIL_POOL_ALLOC_DEFAULT_ALIGN) + { + this->init(initial_elements, new_pool_elements, element_size, alloc_traits, alignment); + } + template + bool free(FunctionInterface * ptr) { + ptr->~FunctionInterface(); + mbed::util::ExtendablePoolAllocator::free(ptr); + return true; + } +}; + +extern ContainerAllocator StaticFPAllocator; +extern ContainerAllocator MemberFPAllocator; +extern ContainerAllocator FunctorFPAllocator; + + +} // detail +} // functional +#endif // FUNCTIONAL_DETAIL_ALLOCATORS_HPP diff --git a/core-util/v2/detail/forward.hpp b/core-util/v2/detail/forward.hpp new file mode 100644 index 0000000..15c1a28 --- /dev/null +++ b/core-util/v2/detail/forward.hpp @@ -0,0 +1,35 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_DETAIL_FORWARD_HPP +#define FUNCTIONAL_DETAIL_FORWARD_HPP + +namespace functional { +namespace detail { +template +T&& forward(typename std::remove_reference::type& a) noexcept +{ + return static_cast(a); +} +template +T&& forward(typename std::remove_reference::type&& a) noexcept +{ + return static_cast(a); +} + +} // namespace detail +} // namespace functional + +#endif // FUNCTIONAL_DETAIL_FORWARD_HPP diff --git a/core-util/v2/detail/functor.hpp b/core-util/v2/detail/functor.hpp new file mode 100644 index 0000000..48b27c6 --- /dev/null +++ b/core-util/v2/detail/functor.hpp @@ -0,0 +1,54 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_DETAIL_FUNCTOR_HPP +#define FUNCTIONAL_DETAIL_FUNCTOR_HPP + +#include "interface.hpp" +#include "allocators.hpp" + +namespace functional { +namespace detail { + + +template +class FunctorContainer; + +template +class FunctorContainer : public FunctionInterface { +public: + FunctorContainer(const F & f) : f(f) {} + FunctorContainer(const FunctorContainer & f) : f(f.f) {} + + virtual ReturnType operator () (ArgTypes&&... Args) { + return f(detail::forward(Args)...); + } + virtual ContainerAllocator * getAllocator() { + return &Allocator; + } + + // virtual void deallocate(FunctionInterface *ptr){ + // (void)ptr; + // this->~FunctionInterface(); + // Allocator.free(this); + // } +protected: + F f; +}; + + +} // namespace detail +} // namespace functional +#endif // FUNCTIONAL_DETAIL_FUNCTOR_HPP diff --git a/core-util/v2/detail/interface.hpp b/core-util/v2/detail/interface.hpp new file mode 100644 index 0000000..6c5771d --- /dev/null +++ b/core-util/v2/detail/interface.hpp @@ -0,0 +1,57 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_V2_DETAIL_INTERFACE_HPP +#define FUNCTIONAL_V2_DETAIL_INTERFACE_HPP + +#include "cmsis.h" +namespace functional { +namespace detail { +template +class FunctionInterface; +} // namespace detail +} // namespace functional + +#include "allocators.hpp" + +namespace functional { +namespace detail { +template +class FunctionInterface { +public: + FunctionInterface() : refcnt(0) {} + virtual ReturnType operator () (ArgTypes&&... Args) = 0; + void inc() { + uint32_t tmp; + do { + tmp = __LDREXW(&refcnt) + 1; + } while (__STREXW(tmp, &refcnt)); + } + bool dec() { + uint32_t tmp; + do { + tmp = __LDREXW(&refcnt) - 1; + } while (__STREXW(tmp, &refcnt)); + return tmp == 0; + } + virtual ContainerAllocator *getAllocator() = 0; +protected: + volatile uint32_t refcnt; +}; + +} // namespace detail +} // namespace functional + +#endif // FUNCTIONAL_V2_DETAIL_INTERFACE_HPP diff --git a/core-util/v2/detail/member.hpp b/core-util/v2/detail/member.hpp new file mode 100644 index 0000000..e458813 --- /dev/null +++ b/core-util/v2/detail/member.hpp @@ -0,0 +1,64 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_DETAIL_MEMBER_HPP +#define FUNCTIONAL_DETAIL_MEMBER_HPP + +#include "interface.hpp" +#include "allocators.hpp" + +namespace functional { +namespace detail { + +template +class MemberContainer; + +template +class MemberContainer : public FunctionInterface { +public: + typedef ReturnType (C::*MemberFPType)(ArgTypes...); + MemberContainer() : obj(nullptr), fp (nullptr) {} + MemberContainer(C * obj, MemberFPType fp) : obj(obj), fp(fp) {} + virtual ReturnType operator () (ArgTypes&&... Args) { + return (obj->*fp)(forward(Args)...); + } + ContainerAllocator * getAllocator() { + return & MemberFPAllocator; + } + + // virtual void deallocate(FunctionInterface *ptr) { + // ptr->~FunctionInterface(); + // StaticFPAllocator.free(ptr); + // } + +protected: + // Forward declaration of an unknown class + class UnknownClass; + // Forward declaration of an unknown member function to this an unknown class + // this kind of declaration is authorized by the standard (see 8.3.3/2 of C++ 03 standard). + // As a result, the compiler will allocate for UnknownFunctionMember_t the biggest size + // and biggest alignment possible for member function. + // This type can be used inside unions, it will help to provide the storage + // with the proper size and alignment guarantees + typedef void (UnknownClass::*UnknownFunctionMember_t)(); + C * obj; + union { + MemberFPType fp; + UnknownFunctionMember_t _alignementAndSizeGuarantees; + }; +}; +} // namespace detail +} // namespace functional +#endif // FUNCTIONAL_DETAIL_MEMBER_HPP diff --git a/core-util/v2/detail/static.hpp b/core-util/v2/detail/static.hpp new file mode 100644 index 0000000..0f59746 --- /dev/null +++ b/core-util/v2/detail/static.hpp @@ -0,0 +1,49 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_DETAIL_STATIC_HPP +#define FUNCTIONAL_DETAIL_STATIC_HPP + +#include "interface.hpp" +#include "allocators.hpp" + +namespace functional { +namespace detail { +template +class StaticContainer; + +template +class StaticContainer : public FunctionInterface { +public: + typedef ReturnType (*staticFPType)(ArgTypes...); + StaticContainer() : fp (nullptr) {} + StaticContainer(staticFPType fp) : fp (fp) {} + virtual ReturnType operator () (ArgTypes&&... Args) { + return fp(forward(Args)...); + } + virtual ContainerAllocator * getAllocator() { + return & StaticFPAllocator; + } + + // virtual void deallocate(FunctionInterface *ptr) { + // ptr->~FunctionInterface(); + // StaticFPAllocator.free(ptr); + // } +protected: + staticFPType fp; +}; +} // namespace detail +} // namespace functional +#endif // FUNCTIONAL_DETAIL_STATIC_HPP diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp new file mode 100644 index 0000000..a755e3a --- /dev/null +++ b/core-util/v2/functional.hpp @@ -0,0 +1,131 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __FUNCTIONAL_HPP__ +#define __FUNCTIONAL_HPP__ + +/** + * \file functional.hpp + * \brief A library that provides function containers, allocated by arbitrary allocators + * + * # Function + * Function is the primary API for functional.hpp + * Function derives from a smart pointer to a functor. + * Function contains almost entirely inline APIs + * + * Functor is a callable class. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core-util/ExtendablePoolAllocator.h" +#include "detail/forward.hpp" +#include "detail/interface.hpp" +#include "detail/static.hpp" +#include "detail/member.hpp" +#include "detail/functor.hpp" +#include "detail/allocators.hpp" + + +namespace functional { + +// namespace detail { +// +// } + +namespace detail { +} // namespace detail + +template +class Function; + +template +class Function { +public: + // Function(Deleter): f(nullptr){} + // template + // Function(const F & f) : FunctionPointer() { + // F * newf = allocator(sizeof(FunctorContainer)) + // new(newf) FunctorContainer(f); + // *this = newf; + // } + // template <> + Function() : ref(nullptr) {} + Function(ReturnType (*f)(ArgTypes...)) { + typedef detail::StaticContainer staticFP; + staticFP * newf = reinterpret_cast(detail::StaticFPAllocator.alloc()); + new(newf) staticFP(f); + ref = newf; + ref->inc(); + } + template + Function(C *o, ReturnType (C::*fp)(ArgTypes...)) { + typedef detail::MemberContainer memberFP; + memberFP * newf = reinterpret_cast(detail::MemberFPAllocator.alloc()); + new(newf) memberFP(o, fp); + ref = newf; + ref->inc(); + } + //TODO: use template metaprogramming to select one of three allocator pools + template + Function(const F &f) { + typedef detail::FunctorContainer FunctorFP; + FunctorFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + new(newf) FunctorFP(f); + ref = newf; + ref->inc(); + } + Function(const Function & f): ref(f.ref) { + ref && ref->inc(); + } + ~Function() { + ref && ref->dec() && ref->getAllocator()->free(ref); + } + Function & operator = (const Function & rhs) { + ref && ref->dec() && ref->getAllocator()->free(ref); + ref = rhs.ref; + ref && ref->inc(); + } + // MemberFunctionContainer< + // } + // template <> + // Function(const Function & rhs) { + // *this = f; + // } + inline ReturnType operator () (ArgTypes&&... Args) { + return (*ref)(detail::forward(Args)...); + } + // inline bool operator == (const Function & rhs) { + // return static_cast(*this) == static_cast(rhs); + // } + // inline Function& operator = (const Function & rhs) { + // static_cast(*this) = static_cast(rhs); + // return *this + // } +protected: + detail::FunctionInterface * ref; +}; + +} // namespace functional + +#endif diff --git a/source/v2/functional.cpp b/source/v2/functional.cpp new file mode 100644 index 0000000..c2d3529 --- /dev/null +++ b/source/v2/functional.cpp @@ -0,0 +1,78 @@ +/* + * PackageLicenseDeclared: Apache-2.0 + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core-util/v2/functional.hpp" + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL +#define STATICFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL +#else +#define STATICFP_INITIAL 8 +#endif + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY +#define STATICFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY +#else +#define STATICFP_GROWBY STATICFP_INITIAL +#endif + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL +#define MEMBERFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL +#else +#define MEMBERFP_INITIAL 8 +#endif + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY +#define MEMBERFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY +#else +#define MEMBERFP_GROWBY MEMBERFP_INITIAL +#endif + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL +#define FUNCTORFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL +#else +#define FUNCTORFP_INITIAL 8 +#endif + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_GROWBY +#define FUNCTORFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_GROWBY +#else +#define FUNCTORFP_GROWBY FUNCTORFP_INITIAL +#endif + +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE +#define FUNCTOR_SIZE YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE +#else +#define FUNCTOR_SIZE (sizeof(MemberContainer) + sizeof(void*) * 3) +#endif + +namespace functional { +namespace detail { +const UAllocTraits_t NeverFreeTrait = {.flags = UALLOC_TRAITS_NEVER_FREE}; + +class UnknownClass; + +ContainerAllocator StaticFPAllocator(STATICFP_INITIAL, + STATICFP_GROWBY, sizeof(StaticContainer), NeverFreeTrait); + +ContainerAllocator MemberFPAllocator(MEMBERFP_INITIAL, + MEMBERFP_GROWBY, sizeof(MemberContainer), NeverFreeTrait); + +ContainerAllocator FunctorFPAllocator(FUNCTORFP_INITIAL, + FUNCTORFP_GROWBY, sizeof(MemberContainer), NeverFreeTrait); + +} // namespace detail +} // namespace functional diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp new file mode 100644 index 0000000..71f42aa --- /dev/null +++ b/test/Functional/main.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 ARM Limited + */ + +#include +#include + + +void testprnt() { + printf("%s\r\n", __PRETTY_FUNCTION__); +} + +class foo { +public: + void testprnt() { + printf("%s\r\n", __PRETTY_FUNCTION__); + } +}; + +void app_start(int , char **) +{ + functional::Function f(testprnt); + f(); + foo o; + functional::Function f2(&o,&foo::testprnt); + f2(); + int i = 1; + functional::Function f3( + [i]() { + printf("%s(%i)\r\n", __PRETTY_FUNCTION__, i); + } + ); + f3(); +} From 095bf879cea9b33c87cb372833491a8341326c47 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 13 Jan 2016 09:52:21 +0000 Subject: [PATCH 02/14] Basic version of bind working --- core-util/v2/functional.hpp | 63 +++++++++++++++++++++++++++++++++++-- test/Functional/main.cpp | 11 +++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index a755e3a..3f23bf4 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -37,8 +37,16 @@ #include #include #include +#include #include "core-util/ExtendablePoolAllocator.h" + +namespace functional { +template +class Function; + +} // namespace functional + #include "detail/forward.hpp" #include "detail/interface.hpp" #include "detail/static.hpp" @@ -54,10 +62,51 @@ namespace functional { // } namespace detail { + +template +struct seq { }; + +template +struct gens : gens { }; + +template +struct gens<0, S...> { + typedef seq type; +}; + + +template +class CapturedArguments; + +template +class CapturedArguments + : public FunctionInterface { +protected: + // typedef typename make_indices<0, CapturedTypes...>::type Indices; + typedef typename gens::type Indicies; +public: + CapturedArguments(Function & f, CapturedTypes&&... CapturedArgs) : + f(f), storage(CapturedArgs...) + {} + + virtual ReturnType operator () (ArgTypes&&... Args) { + return idxcall(typename gens::type(),forward(Args)...); + } + template + inline ReturnType idxcall(seq, ArgTypes&&... Args) { + return f(forward(Args)..., forward(std::get(storage))...); + } + + virtual ContainerAllocator * getAllocator() { + return & Allocator; + } +protected: + + Function & f; + std::tuple storage; +}; } // namespace detail -template -class Function; template class Function { @@ -98,6 +147,16 @@ class Function { Function(const Function & f): ref(f.ref) { ref && ref->inc(); } + template + Function(Function & f, CapturedTypes&&... CapturedArgs) { + typedef typename detail::CapturedArguments CaptureFP; + CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + new(newf) CaptureFP(f, std::forward(CapturedArgs)...); + ref = newf; + ref->inc(); + } + + ~Function() { ref && ref->dec() && ref->getAllocator()->free(ref); } diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 71f42aa..965e759 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -9,6 +9,9 @@ void testprnt() { printf("%s\r\n", __PRETTY_FUNCTION__); } +void testprnt3(int i, int j, int k) { + printf("%s(%i, %i, %i)\r\n", __PRETTY_FUNCTION__, i, j, k); +} class foo { public: @@ -31,4 +34,12 @@ void app_start(int , char **) } ); f3(); + functional::Function f4(testprnt3); + f4(1,2,3); + functional::Function f5(f4,4); + f5(1,2); + functional::Function f6(f4,4,5); + f6(1); + functional::Function f7(f4,4,5,6); + f7(); } From bc0df166a32610b270471c475d7d4ed185fd5ce5 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Thu, 14 Jan 2016 10:31:26 +0000 Subject: [PATCH 03/14] =?UTF-8?q?Partial=20Bind=20(doesn=E2=80=99t=20work?= =?UTF-8?q?=20yet)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core-util/v2/functional.hpp | 76 ++++++++++++++++++++++++++----------- source/v2/functional.cpp | 2 +- test/Functional/main.cpp | 12 +++--- 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 3f23bf4..5494071 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -62,18 +62,19 @@ namespace functional { // } namespace detail { - -template -struct seq { }; -template -struct gens : gens { }; +namespace index { +template +struct sequence { }; -template -struct gens<0, S...> { - typedef seq type; -}; +template +struct generator : generator { }; +template +struct generator<0, S...> { + typedef sequence type; +}; +} // namespace index template class CapturedArguments; @@ -81,20 +82,20 @@ class CapturedArguments; template class CapturedArguments : public FunctionInterface { -protected: - // typedef typename make_indices<0, CapturedTypes...>::type Indices; - typedef typename gens::type Indicies; public: - CapturedArguments(Function & f, CapturedTypes&&... CapturedArgs) : - f(f), storage(CapturedArgs...) + // CapturedArguments(Function & f, CapturedTypes&&... CapturedArgs) : + // f(f), storage(CapturedArgs...) + // {} + CapturedArguments(const std::tuple& t, Function& f): + f(f), storage(t) {} virtual ReturnType operator () (ArgTypes&&... Args) { - return idxcall(typename gens::type(),forward(Args)...); + return idxcall(typename index::generator::type(), forward(Args)...); } - template - inline ReturnType idxcall(seq, ArgTypes&&... Args) { - return f(forward(Args)..., forward(std::get(storage))...); + template + inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { + return f(forward(std::get(storage))..., forward(Args)...); } virtual ContainerAllocator * getAllocator() { @@ -102,9 +103,25 @@ class CapturedArguments } protected: - Function & f; + Function & f; std::tuple storage; }; + +#include +template struct RemoveArgs {}; + +template +struct RemoveArgs { + typedef Function type; +}; + +template +struct RemoveArgs { + static_assert(std::is_same::value, "Type mismatch in argument removal"); + typedef typename RemoveArgs::type type; +}; + + } // namespace detail @@ -147,15 +164,30 @@ class Function { Function(const Function & f): ref(f.ref) { ref && ref->inc(); } + // template + // Function(Function & f, CapturedTypes&&... CapturedArgs) { + // typedef typename detail::CapturedArguments CaptureFP; + // std::tuple t(detail::forward(CapturedArgs)...); + // CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + // new(newf) CaptureFP(t, f); + // ref = newf; + // ref->inc(); + // } template - Function(Function & f, CapturedTypes&&... CapturedArgs) { + Function(std::tuple& t, Function& f) { typedef typename detail::CapturedArguments CaptureFP; + static_assert(sizeof(CaptureFP) <= 40, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); - new(newf) CaptureFP(f, std::forward(CapturedArgs)...); + new(newf) CaptureFP(t, f); ref = newf; ref->inc(); } - + template + typename detail::RemoveArgs::type bind(CapturedTypes... CapturedArgs) { + std::tuple t(detail::forward(CapturedArgs)...); + typename detail::RemoveArgs::type f(t,*this); + return f; + } ~Function() { ref && ref->dec() && ref->getAllocator()->free(ref); diff --git a/source/v2/functional.cpp b/source/v2/functional.cpp index c2d3529..fcf3feb 100644 --- a/source/v2/functional.cpp +++ b/source/v2/functional.cpp @@ -72,7 +72,7 @@ ContainerAllocator MemberFPAllocator(MEMBERFP_INITIAL, MEMBERFP_GROWBY, sizeof(MemberContainer), NeverFreeTrait); ContainerAllocator FunctorFPAllocator(FUNCTORFP_INITIAL, - FUNCTORFP_GROWBY, sizeof(MemberContainer), NeverFreeTrait); + FUNCTORFP_GROWBY, FUNCTOR_SIZE, NeverFreeTrait); } // namespace detail } // namespace functional diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 965e759..4670454 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -36,10 +36,12 @@ void app_start(int , char **) f3(); functional::Function f4(testprnt3); f4(1,2,3); - functional::Function f5(f4,4); + static_assert(std::is_same, + functional::detail::RemoveArgs::type >::value, "oops"); + functional::Function f5 = f4.bind(4); f5(1,2); - functional::Function f6(f4,4,5); - f6(1); - functional::Function f7(f4,4,5,6); - f7(); + // functional::Function f6 = f4.bind(4,5); + // f6(1); + // functional::Function f7 = f4.bind(4,5,6); + // f7(); } From e1ddf38a3f209ece1045d80a434be727dbbb3e27 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Thu, 14 Jan 2016 11:32:02 +0000 Subject: [PATCH 04/14] First-argument partial-binding now working --- core-util/v2/detail/interface.hpp | 3 ++- core-util/v2/functional.hpp | 4 ++-- test/Functional/main.cpp | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core-util/v2/detail/interface.hpp b/core-util/v2/detail/interface.hpp index 6c5771d..5ce13e5 100644 --- a/core-util/v2/detail/interface.hpp +++ b/core-util/v2/detail/interface.hpp @@ -33,11 +33,12 @@ class FunctionInterface { public: FunctionInterface() : refcnt(0) {} virtual ReturnType operator () (ArgTypes&&... Args) = 0; - void inc() { + bool inc() { uint32_t tmp; do { tmp = __LDREXW(&refcnt) + 1; } while (__STREXW(tmp, &refcnt)); + return true; } bool dec() { uint32_t tmp; diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 5494071..4963319 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -173,8 +173,8 @@ class Function { // ref = newf; // ref->inc(); // } - template - Function(std::tuple& t, Function& f) { + template + Function(std::tuple& t, Function& f) { typedef typename detail::CapturedArguments CaptureFP; static_assert(sizeof(CaptureFP) <= 40, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 4670454..969d558 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -40,8 +40,8 @@ void app_start(int , char **) functional::detail::RemoveArgs::type >::value, "oops"); functional::Function f5 = f4.bind(4); f5(1,2); - // functional::Function f6 = f4.bind(4,5); - // f6(1); - // functional::Function f7 = f4.bind(4,5,6); - // f7(); + functional::Function f6 = f4.bind(4,5); + f6(1); + functional::Function f7 = f4.bind(4,5,6); + f7(); } From 220d61ba54f68e9f919593ae8eec3ebcc78642d4 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Thu, 14 Jan 2016 16:26:43 +0000 Subject: [PATCH 05/14] Move capture into its own header --- core-util/v2/detail/capture.hpp | 94 +++++++++++++++++++++++++++++++++ core-util/v2/functional.hpp | 65 +---------------------- 2 files changed, 95 insertions(+), 64 deletions(-) create mode 100644 core-util/v2/detail/capture.hpp diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp new file mode 100644 index 0000000..44aed9b --- /dev/null +++ b/core-util/v2/detail/capture.hpp @@ -0,0 +1,94 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FUNCTIONAL_DETAIL_CAPTURE_HPP +#define FUNCTIONAL_DETAIL_CAPTURE_HPP + +#include +#include + +#include "interface.hpp" +#include "allocators.hpp" + +namespace functional { +namespace detail { + + +namespace index { +template +struct sequence { }; + +template +struct generator : generator { }; + +template +struct generator<0, S...> { + typedef sequence type; +}; +} // namespace index + +template +class CapturedArguments; + +template +class CapturedArguments + : public FunctionInterface { +public: + // CapturedArguments(Function & f, CapturedTypes&&... CapturedArgs) : + // f(f), storage(CapturedArgs...) + // {} + CapturedArguments(const std::tuple& t, Function& f): + f(f), storage(t) + {} + + virtual ReturnType operator () (ArgTypes&&... Args) { + return idxcall(typename index::generator::type(), forward(Args)...); + } + template + inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { + return f(forward(std::get(storage))..., forward(Args)...); + } + + virtual ContainerAllocator * getAllocator() { + return & Allocator; + } +protected: + /* + * Future Optimization Note: It is possible to reduce memory consumption and call + * overhead by making CapturedArguments inherit from each of the FunctionInterface + * types, rather than just from FunctionInterface. + * + * In this case, however, a smarter allocator would help + */ + Function & f; + std::tuple storage; +}; + +template struct RemoveArgs {}; + +template +struct RemoveArgs { + typedef Function type; +}; + +template +struct RemoveArgs { + static_assert(std::is_same::value, "Type mismatch in argument removal"); + typedef typename RemoveArgs::type type; +}; + +} // namespace detail +} // namespace functional +#endif // FUNCTIONAL_DETAIL_CAPTURE_HPP diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 4963319..aa1e2ca 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -32,12 +32,10 @@ #include #include #include -#include #include #include #include #include -#include #include "core-util/ExtendablePoolAllocator.h" @@ -53,73 +51,12 @@ class Function; #include "detail/member.hpp" #include "detail/functor.hpp" #include "detail/allocators.hpp" +#include "detail/capture.hpp" namespace functional { - -// namespace detail { -// -// } - namespace detail { -namespace index { -template -struct sequence { }; - -template -struct generator : generator { }; - -template -struct generator<0, S...> { - typedef sequence type; -}; -} // namespace index - -template -class CapturedArguments; - -template -class CapturedArguments - : public FunctionInterface { -public: - // CapturedArguments(Function & f, CapturedTypes&&... CapturedArgs) : - // f(f), storage(CapturedArgs...) - // {} - CapturedArguments(const std::tuple& t, Function& f): - f(f), storage(t) - {} - - virtual ReturnType operator () (ArgTypes&&... Args) { - return idxcall(typename index::generator::type(), forward(Args)...); - } - template - inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { - return f(forward(std::get(storage))..., forward(Args)...); - } - - virtual ContainerAllocator * getAllocator() { - return & Allocator; - } -protected: - - Function & f; - std::tuple storage; -}; - -#include -template struct RemoveArgs {}; - -template -struct RemoveArgs { - typedef Function type; -}; - -template -struct RemoveArgs { - static_assert(std::is_same::value, "Type mismatch in argument removal"); - typedef typename RemoveArgs::type type; -}; } // namespace detail From 2ffe6366e0392ff5e72e630f6a3280fc39a7e771 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Thu, 14 Jan 2016 16:42:58 +0000 Subject: [PATCH 06/14] Add definitions for duck-type compatibility --- core-util/v2/functional.hpp | 81 +++++++++++++++++++------------------ test/Functional/main.cpp | 6 +-- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index aa1e2ca..3199448 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -34,10 +34,8 @@ #include #include #include -#include #include -#include "core-util/ExtendablePoolAllocator.h" namespace functional { template @@ -53,26 +51,11 @@ class Function; #include "detail/allocators.hpp" #include "detail/capture.hpp" - namespace functional { -namespace detail { - - - -} // namespace detail - template class Function { public: - // Function(Deleter): f(nullptr){} - // template - // Function(const F & f) : FunctionPointer() { - // F * newf = allocator(sizeof(FunctorContainer)) - // new(newf) FunctorContainer(f); - // *this = newf; - // } - // template <> Function() : ref(nullptr) {} Function(ReturnType (*f)(ArgTypes...)) { typedef detail::StaticContainer staticFP; @@ -81,6 +64,10 @@ class Function { ref = newf; ref->inc(); } + void attach(ReturnType (*f)(ArgTypes...)) { + Function func(f); + *this = func; + } template Function(C *o, ReturnType (C::*fp)(ArgTypes...)) { typedef detail::MemberContainer memberFP; @@ -89,7 +76,13 @@ class Function { ref = newf; ref->inc(); } - //TODO: use template metaprogramming to select one of three allocator pools + template + void attach(C *o, ReturnType (C::*fp)(ArgTypes...)) { + Function func(o,fp); + *this = func; + } + + //Optimization Note: use template metaprogramming to select one of three allocator pools template Function(const F &f) { typedef detail::FunctorContainer FunctorFP; @@ -98,18 +91,15 @@ class Function { ref = newf; ref->inc(); } + template + void attach(const F & f) { + Function func(f); + *this = func; + } Function(const Function & f): ref(f.ref) { ref && ref->inc(); } - // template - // Function(Function & f, CapturedTypes&&... CapturedArgs) { - // typedef typename detail::CapturedArguments CaptureFP; - // std::tuple t(detail::forward(CapturedArgs)...); - // CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); - // new(newf) CaptureFP(t, f); - // ref = newf; - // ref->inc(); - // } + template Function(std::tuple& t, Function& f) { typedef typename detail::CapturedArguments CaptureFP; @@ -119,6 +109,7 @@ class Function { ref = newf; ref->inc(); } + template typename detail::RemoveArgs::type bind(CapturedTypes... CapturedArgs) { std::tuple t(detail::forward(CapturedArgs)...); @@ -134,26 +125,36 @@ class Function { ref = rhs.ref; ref && ref->inc(); } - // MemberFunctionContainer< - // } - // template <> - // Function(const Function & rhs) { - // *this = f; - // } inline ReturnType operator () (ArgTypes&&... Args) { return (*ref)(detail::forward(Args)...); } - // inline bool operator == (const Function & rhs) { - // return static_cast(*this) == static_cast(rhs); - // } - // inline Function& operator = (const Function & rhs) { - // static_cast(*this) = static_cast(rhs); - // return *this - // } + inline ReturnType call(ArgTypes&&... Args) { + return (*ref)(detail::forward(Args)...); + } protected: detail::FunctionInterface * ref; }; +namespace v0_compatibility { +/* This namespace contains the class definitions for duck-type compatibility + * with v0 FunctionPointers + */ + +template +class FunctionPointer0 : public Function {}; + +template +class FunctionPointer1 : public Function {}; + +template +class FunctionPointer2 : public Function {}; + +template +class FunctionPointer3 : public Function {}; + +typedef FunctionPointer0 FunctionPointer; +} // namespace v0_compatibility + } // namespace functional #endif diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 969d558..00ae911 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -2,9 +2,9 @@ * Copyright (c) 2015 ARM Limited */ -#include -#include - +#include +#include "core-util/v2/functional.hpp" +#include "mbed-drivers/app.h" void testprnt() { printf("%s\r\n", __PRETTY_FUNCTION__); From 15c84838432a6405cafaf935bb9204ff3568c9a1 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 20 Jan 2016 15:38:45 +0000 Subject: [PATCH 07/14] Updates to Function design This commit adds several updates to the Function implementation * bind_first and bind_last both work * APIs changed to snake_case * atomic_incr and atomic_decr are now used instead of Cortex-M3+ primitives * The first bits of documentation have been added --- core-util/v2/detail/allocators.hpp | 17 ++++++ core-util/v2/detail/capture.hpp | 75 ++++++++++++++++++++---- core-util/v2/detail/functor.hpp | 2 +- core-util/v2/detail/interface.hpp | 24 +++----- core-util/v2/detail/member.hpp | 2 +- core-util/v2/detail/static.hpp | 2 +- core-util/v2/functional.hpp | 92 +++++++++++++++++++++++------- source/v2/functional.cpp | 6 -- test/Functional/main.cpp | 14 +++-- 9 files changed, 172 insertions(+), 62 deletions(-) diff --git a/core-util/v2/detail/allocators.hpp b/core-util/v2/detail/allocators.hpp index d64d0e3..fe5cacb 100644 --- a/core-util/v2/detail/allocators.hpp +++ b/core-util/v2/detail/allocators.hpp @@ -38,6 +38,23 @@ class ContainerAllocator : public mbed::util::ExtendablePoolAllocator { } }; +#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE +#define FUNCTOR_SIZE YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE +#else +/* + * Optimization note: This size was chosen to allow a Function to bind a DNS response. + * 4 bytes for the vtable pointers + * 4 bytes for the base Function + * 128/8 bytes for an IPv6 address + * 4 bytes for a char* pointer. + * 4 bytes of padding to round out to 8-byte alignment. + * + * This size should be optimized further by examining application requirements. + */ +#define FUNCTOR_SIZE (4 + 4 + (128/8) + 4 + 4) +#endif + + extern ContainerAllocator StaticFPAllocator; extern ContainerAllocator MemberFPAllocator; extern ContainerAllocator FunctorFPAllocator; diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp index 44aed9b..35b5cce 100644 --- a/core-util/v2/detail/capture.hpp +++ b/core-util/v2/detail/capture.hpp @@ -40,16 +40,13 @@ struct generator<0, S...> { } // namespace index template -class CapturedArguments; +class CaptureFirst; template -class CapturedArguments +class CaptureFirst : public FunctionInterface { public: - // CapturedArguments(Function & f, CapturedTypes&&... CapturedArgs) : - // f(f), storage(CapturedArgs...) - // {} - CapturedArguments(const std::tuple& t, Function& f): + CaptureFirst(const std::tuple& t, Function& f): f(f), storage(t) {} @@ -61,13 +58,13 @@ class CapturedArguments return f(forward(std::get(storage))..., forward(Args)...); } - virtual ContainerAllocator * getAllocator() { + virtual ContainerAllocator * get_allocator() { return & Allocator; } protected: /* * Future Optimization Note: It is possible to reduce memory consumption and call - * overhead by making CapturedArguments inherit from each of the FunctionInterface + * overhead by making CaptureFirst inherit from each of the FunctionInterface * types, rather than just from FunctionInterface. * * In this case, however, a smarter allocator would help @@ -76,19 +73,73 @@ class CapturedArguments std::tuple storage; }; -template struct RemoveArgs {}; +template +class CaptureLast; + +template +class CaptureLast + : public FunctionInterface { +public: + CaptureLast(Function & f, CapturedTypes&&... CapturedArgs) : + f(f), storage(CapturedArgs...) + {} + + virtual ReturnType operator () (ArgTypes&&... Args) { + return idxcall(typename index::generator::type(), forward(Args)...); + } + template + inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { + return f(forward(Args)..., forward(std::get(storage))...); + } + + virtual ContainerAllocator * get_allocator() { + return & Allocator; + } +protected: + + Function & f; + std::tuple storage; +}; + +template struct RemoveFirstArgs {}; template -struct RemoveArgs { +struct RemoveFirstArgs { typedef Function type; }; template -struct RemoveArgs { +struct RemoveFirstArgs { static_assert(std::is_same::value, "Type mismatch in argument removal"); - typedef typename RemoveArgs::type type; + typedef typename RemoveFirstArgs::type type; }; +template struct RemoveLastArgs; + +template +struct RemoveLastArgs { + using type = void; +}; + +template +struct RemoveLastArgs { + using type = typename std::conditional< + std::is_same,std::tuple >::value, + Function, + typename RemoveLastArgs::type + >::type; +}; + +template +Function bind_last(Function &&, Function& f, CapturedTypes... CapturedArgs) { + using CaptureFP = CaptureLast; + static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); + CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + new(newf) CaptureFP(f,forward(CapturedArgs)...); + return Function(static_cast*>(newf)); +} + + } // namespace detail } // namespace functional #endif // FUNCTIONAL_DETAIL_CAPTURE_HPP diff --git a/core-util/v2/detail/functor.hpp b/core-util/v2/detail/functor.hpp index 48b27c6..4cd7971 100644 --- a/core-util/v2/detail/functor.hpp +++ b/core-util/v2/detail/functor.hpp @@ -35,7 +35,7 @@ class FunctorContainer : public Function virtual ReturnType operator () (ArgTypes&&... Args) { return f(detail::forward(Args)...); } - virtual ContainerAllocator * getAllocator() { + virtual ContainerAllocator * get_allocator() { return &Allocator; } diff --git a/core-util/v2/detail/interface.hpp b/core-util/v2/detail/interface.hpp index 5ce13e5..0aa2fb1 100644 --- a/core-util/v2/detail/interface.hpp +++ b/core-util/v2/detail/interface.hpp @@ -16,7 +16,7 @@ #ifndef FUNCTIONAL_V2_DETAIL_INTERFACE_HPP #define FUNCTIONAL_V2_DETAIL_INTERFACE_HPP -#include "cmsis.h" +#include "core-util/atomic_ops.h" namespace functional { namespace detail { template @@ -33,23 +33,17 @@ class FunctionInterface { public: FunctionInterface() : refcnt(0) {} virtual ReturnType operator () (ArgTypes&&... Args) = 0; - bool inc() { - uint32_t tmp; - do { - tmp = __LDREXW(&refcnt) + 1; - } while (__STREXW(tmp, &refcnt)); - return true; + virtual ContainerAllocator *get_allocator() = 0; + inline uint32_t inc() + { + return mbed::util::atomic_incr(&refcnt, 1ul); } - bool dec() { - uint32_t tmp; - do { - tmp = __LDREXW(&refcnt) - 1; - } while (__STREXW(tmp, &refcnt)); - return tmp == 0; + inline uint32_t dec() + { + return mbed::util::atomic_decr(&refcnt, 1ul); } - virtual ContainerAllocator *getAllocator() = 0; protected: - volatile uint32_t refcnt; + uint32_t refcnt; }; } // namespace detail diff --git a/core-util/v2/detail/member.hpp b/core-util/v2/detail/member.hpp index e458813..ea6bd78 100644 --- a/core-util/v2/detail/member.hpp +++ b/core-util/v2/detail/member.hpp @@ -34,7 +34,7 @@ class MemberContainer : public FunctionInterface *fp)(forward(Args)...); } - ContainerAllocator * getAllocator() { + ContainerAllocator * get_allocator() { return & MemberFPAllocator; } diff --git a/core-util/v2/detail/static.hpp b/core-util/v2/detail/static.hpp index 0f59746..de5c93c 100644 --- a/core-util/v2/detail/static.hpp +++ b/core-util/v2/detail/static.hpp @@ -33,7 +33,7 @@ class StaticContainer : public FunctionInterface (Args)...); } - virtual ContainerAllocator * getAllocator() { + virtual ContainerAllocator * get_allocator() { return & StaticFPAllocator; } diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 3199448..ed25765 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -1,5 +1,5 @@ /* mbed Microcontroller Library - * Copyright (c) 2006-2015 ARM Limited + * Copyright (c) 2006-2016 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,15 +18,15 @@ /** * \file functional.hpp - * \brief A library that provides function containers, allocated by arbitrary allocators + * \brief A library that provides a ```std::function```-like interface for containing reference to callables. * - * # Function - * Function is the primary API for functional.hpp - * Function derives from a smart pointer to a functor. - * Function contains almost entirely inline APIs - * - * Functor is a callable class. + * The primary API for containing and referring to callables is ```Function```. APIs in the ```detail``` namespace + * should not be invoked directly. * + * In order to maintain compatibility with existing code, ```Function``` is intended to be duck-type compatible with + * the ```FunctionPointer``` family of classes, but it is intentionally not named the same way. A family of + * compatibility classes is provided to support conversion from ```FunctionPointer``` code to ```Function```. + * These compatibility classes will be deprecated in an upcoming release. */ #include @@ -35,7 +35,7 @@ #include #include #include - +#include "core-util/atomic_ops.h" namespace functional { template @@ -52,11 +52,36 @@ class Function; #include "detail/capture.hpp" namespace functional { - +/** + * Superficially, ```Function``` is intented to appear similar to ```std::function```. However, ```Function``` has a + * number of key differences. ```Function``` uses reference counting to limit the copying of large callable objects, + * such as lambdas with large capture lists or ```Function```s with large or many arguments. ```Function``` also uses + * pool allocation so that objects can be created in interrupt context without using ```malloc()```. These choices are + * to overcome two specific limitations of ```std::function``` + * + * 1. Copy-constructing a functor requires copy-constructing its member objects. In the case of lambdas, this means + * copy-constructing the lambda captures. lambda captures are not guaranteed to have reentrant copy constructors, so + * lambdas cannot be copy-constructed in interrupt context. Therefore, functors must be created in dynamic memory and + * only pointers to functors can be used. + * 2. Due to 1. creating a functor requires dynamic memory allocation, however malloc is not permitted in interrupt + * context. As a result functors must be pool allocated. + * 3. In order to simplify memory management of functors and due to 1. and 2., functors are reference-counted. + */ template class Function { public: + /** + * The empty constructor. + * Since ```Function``` only contains a single pointer, only a null assignment is necessary. + */ Function() : ref(nullptr) {} + Function(detail::FunctionInterface *f) { + ref = f; + if (ref) { + ref->inc(); + } + } + Function(ReturnType (*f)(ArgTypes...)) { typedef detail::StaticContainer staticFP; staticFP * newf = reinterpret_cast(detail::StaticFPAllocator.alloc()); @@ -82,6 +107,7 @@ class Function { *this = func; } + //Optimization Note: use template metaprogramming to select one of three allocator pools template Function(const F &f) { @@ -97,33 +123,59 @@ class Function { *this = func; } Function(const Function & f): ref(f.ref) { - ref && ref->inc(); + if(ref) { + ref->inc(); + } } template Function(std::tuple& t, Function& f) { - typedef typename detail::CapturedArguments CaptureFP; - static_assert(sizeof(CaptureFP) <= 40, "Size of bound arguments is too large" ); + typedef typename detail::CaptureFirst CaptureFP; + static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); new(newf) CaptureFP(t, f); ref = newf; ref->inc(); } + // Optimization note: This should be changed to use the same constructor as bind_last. template - typename detail::RemoveArgs::type bind(CapturedTypes... CapturedArgs) { + typename detail::RemoveFirstArgs::type bind_first( + CapturedTypes... CapturedArgs) + { std::tuple t(detail::forward(CapturedArgs)...); - typename detail::RemoveArgs::type f(t,*this); + typename detail::RemoveFirstArgs::type f(t,*this); + return f; + } + + template + typename detail::RemoveLastArgs::type bind_last(CapturedTypes... CapturedArgs) + { + using ReturnFP = typename detail::RemoveLastArgs::type; + static_assert(std::is_same >::value, "oops"); + ReturnFP f(detail::bind_last(ReturnFP(), *this, detail::forward(CapturedArgs)...)); return f; } - ~Function() { - ref && ref->dec() && ref->getAllocator()->free(ref); + ~Function() + { + if (ref) { + if (ref->dec() == 0) { + ref->get_allocator()->free(ref); + } + } } - Function & operator = (const Function & rhs) { - ref && ref->dec() && ref->getAllocator()->free(ref); + Function & operator = (const Function & rhs) + { + if (ref) { + if (ref->dec() == 0) { + ref->get_allocator()->free(ref); + } + } ref = rhs.ref; - ref && ref->inc(); + if(ref) { + ref->inc(); + } } inline ReturnType operator () (ArgTypes&&... Args) { return (*ref)(detail::forward(Args)...); diff --git a/source/v2/functional.cpp b/source/v2/functional.cpp index fcf3feb..05b8214 100644 --- a/source/v2/functional.cpp +++ b/source/v2/functional.cpp @@ -53,12 +53,6 @@ #define FUNCTORFP_GROWBY FUNCTORFP_INITIAL #endif -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE -#define FUNCTOR_SIZE YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE -#else -#define FUNCTOR_SIZE (sizeof(MemberContainer) + sizeof(void*) * 3) -#endif - namespace functional { namespace detail { const UAllocTraits_t NeverFreeTrait = {.flags = UALLOC_TRAITS_NEVER_FREE}; diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 00ae911..83d7d9c 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -2,9 +2,11 @@ * Copyright (c) 2015 ARM Limited */ +/* + * NOTE: this file should be removed prior to merge since it is not adequate as a test. + */ #include #include "core-util/v2/functional.hpp" -#include "mbed-drivers/app.h" void testprnt() { printf("%s\r\n", __PRETTY_FUNCTION__); @@ -36,12 +38,12 @@ void app_start(int , char **) f3(); functional::Function f4(testprnt3); f4(1,2,3); - static_assert(std::is_same, - functional::detail::RemoveArgs::type >::value, "oops"); - functional::Function f5 = f4.bind(4); + functional::Function f5 = f4.bind_first(4); f5(1,2); - functional::Function f6 = f4.bind(4,5); + functional::Function f6 = f4.bind_first(4,5); f6(1); - functional::Function f7 = f4.bind(4,5,6); + functional::Function f7 = f4.bind_first(4,5,6); f7(); + functional::Function f8 = f4.bind_last(4); + f8(1,2); } From 27e5e06f0a3ff7c332a0c2903270bad2d0344b7a Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Fri, 22 Jan 2016 10:38:59 +0000 Subject: [PATCH 08/14] Review updates: * Explicit call to ```ExtendablePoolAllocator()``` * Remove unnecessary virtuals * Add asserts for memory allocation failure --- core-util/v2/detail/allocators.hpp | 1 + core-util/v2/detail/capture.hpp | 10 +++++----- core-util/v2/functional.hpp | 19 ++++++++++++++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/core-util/v2/detail/allocators.hpp b/core-util/v2/detail/allocators.hpp index fe5cacb..c3bc9ba 100644 --- a/core-util/v2/detail/allocators.hpp +++ b/core-util/v2/detail/allocators.hpp @@ -27,6 +27,7 @@ class ContainerAllocator : public mbed::util::ExtendablePoolAllocator { public: ContainerAllocator(size_t initial_elements, size_t new_pool_elements, size_t element_size, UAllocTraits_t alloc_traits, unsigned alignment = MBED_UTIL_POOL_ALLOC_DEFAULT_ALIGN) + : mbed::util::ExtendablePoolAllocator() { this->init(initial_elements, new_pool_elements, element_size, alloc_traits, alignment); } diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp index 35b5cce..2748604 100644 --- a/core-util/v2/detail/capture.hpp +++ b/core-util/v2/detail/capture.hpp @@ -16,7 +16,6 @@ #ifndef FUNCTIONAL_DETAIL_CAPTURE_HPP #define FUNCTIONAL_DETAIL_CAPTURE_HPP -#include #include #include "interface.hpp" @@ -50,7 +49,7 @@ class CaptureFirst f(f), storage(t) {} - virtual ReturnType operator () (ArgTypes&&... Args) { + ReturnType operator () (ArgTypes&&... Args) { return idxcall(typename index::generator::type(), forward(Args)...); } template @@ -58,7 +57,7 @@ class CaptureFirst return f(forward(std::get(storage))..., forward(Args)...); } - virtual ContainerAllocator * get_allocator() { + ContainerAllocator * get_allocator() { return & Allocator; } protected: @@ -84,7 +83,7 @@ class CaptureLast f(f), storage(CapturedArgs...) {} - virtual ReturnType operator () (ArgTypes&&... Args) { + ReturnType operator () (ArgTypes&&... Args) { return idxcall(typename index::generator::type(), forward(Args)...); } template @@ -92,7 +91,7 @@ class CaptureLast return f(forward(Args)..., forward(std::get(storage))...); } - virtual ContainerAllocator * get_allocator() { + ContainerAllocator * get_allocator() { return & Allocator; } protected: @@ -135,6 +134,7 @@ Function bind_last(Function && using CaptureFP = CaptureLast; static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) CaptureFP(f,forward(CapturedArgs)...); return Function(static_cast*>(newf)); } diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index ed25765..4faa89c 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -34,7 +34,6 @@ #include #include #include -#include #include "core-util/atomic_ops.h" namespace functional { @@ -75,16 +74,31 @@ class Function { * Since ```Function``` only contains a single pointer, only a null assignment is necessary. */ Function() : ref(nullptr) {} + /** + * Construct a Function from a FunctionInterface. + * + * This is an API that should only be used by Function and its helpers. It was specifically added to enable + * bind_first and bind_last. + * + * @param[in] f + */ Function(detail::FunctionInterface *f) { ref = f; if (ref) { ref->inc(); } } + /** + * Move constructor + * This constructor steals the reference from the rvalue-reference Function + * without incrementing the reference count. + */ + Function(Function &&f): ref(f.ref) {} Function(ReturnType (*f)(ArgTypes...)) { typedef detail::StaticContainer staticFP; staticFP * newf = reinterpret_cast(detail::StaticFPAllocator.alloc()); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) staticFP(f); ref = newf; ref->inc(); @@ -97,6 +111,7 @@ class Function { Function(C *o, ReturnType (C::*fp)(ArgTypes...)) { typedef detail::MemberContainer memberFP; memberFP * newf = reinterpret_cast(detail::MemberFPAllocator.alloc()); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) memberFP(o, fp); ref = newf; ref->inc(); @@ -113,6 +128,7 @@ class Function { Function(const F &f) { typedef detail::FunctorContainer FunctorFP; FunctorFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) FunctorFP(f); ref = newf; ref->inc(); @@ -133,6 +149,7 @@ class Function { typedef typename detail::CaptureFirst CaptureFP; static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) CaptureFP(t, f); ref = newf; ref->inc(); From e014f397f8c043972cc5f09f904a957ba85f5b53 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Mon, 25 Jan 2016 10:28:10 +0000 Subject: [PATCH 09/14] Add support APIs for C function escalation Supports https://github.com/ARMmbed/sockets/issues/58 * Add an API to obtain the internal reference as a ```void *``` * Add an API to call a Function from a ```void *``` reference * Add an API to call a Function from a ```void *``` reference, then decrement the reference count --- core-util/v2/functional.hpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 4faa89c..4a42c4a 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -35,6 +35,7 @@ #include #include #include "core-util/atomic_ops.h" +#include "core-util/assert.h" namespace functional { template @@ -200,6 +201,41 @@ class Function { inline ReturnType call(ArgTypes&&... Args) { return (*ref)(detail::forward(Args)...); } + void * get_ref() { + if (ref) + ref->inc(); + return reinterpret_cast(ref); + } + /* Note that this function does not use rvalue references beceause it is for C API interoperability */ + static ReturnType call_from_void(void *vref, ArgTypes... Args) { + detail::FunctionInterface * sref = + reinterpret_cast *>(vref); + CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); + return (*sref)(Args...); + } + static ReturnType call_from_void_rref(void *vref, ArgTypes&&... Args) { + detail::FunctionInterface * sref = + reinterpret_cast *>(vref); + CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); + return (*sref)(detail::forward(Args)...); + } + static ReturnType call_from_void_dec(void *vref, ArgTypes... Args) { + detail::FunctionInterface * sref = + reinterpret_cast *>(vref); + CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); + ReturnType r((*sref)(Args...)); + sref->dec(); + return r; + } + static ReturnType call_from_void_dec_rref(void *vref, ArgTypes&&... Args) { + detail::FunctionInterface * sref = + reinterpret_cast *>(vref); + CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); + ReturnType r((*sref)(detail::forward(Args)...)); + sref->dec(); + return r; + } + protected: detail::FunctionInterface * ref; }; From ae0ad5e6ba45c3117fc17cb466d17beffb5d99d9 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 3 Feb 2016 11:57:09 +0000 Subject: [PATCH 10/14] Move library polyfill items to polyfill * create a polyfill namespace * move forward to polyfill * Add a sorted tuple --- core-util/v2/detail/capture.hpp | 20 ++--- core-util/v2/detail/forward.hpp | 35 -------- core-util/v2/detail/functor.hpp | 2 +- core-util/v2/detail/member.hpp | 3 +- core-util/v2/detail/polyfill.hpp | 133 +++++++++++++++++++++++++++++++ core-util/v2/detail/static.hpp | 3 +- core-util/v2/functional.hpp | 16 ++-- 7 files changed, 156 insertions(+), 56 deletions(-) delete mode 100644 core-util/v2/detail/forward.hpp create mode 100644 core-util/v2/detail/polyfill.hpp diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp index 2748604..f19a2bc 100644 --- a/core-util/v2/detail/capture.hpp +++ b/core-util/v2/detail/capture.hpp @@ -16,7 +16,7 @@ #ifndef FUNCTIONAL_DETAIL_CAPTURE_HPP #define FUNCTIONAL_DETAIL_CAPTURE_HPP -#include +#include "polyfill.hpp" #include "interface.hpp" #include "allocators.hpp" @@ -45,16 +45,16 @@ template : public FunctionInterface { public: - CaptureFirst(const std::tuple& t, Function& f): + CaptureFirst(const polyfill::tuple& t, Function& f): f(f), storage(t) {} ReturnType operator () (ArgTypes&&... Args) { - return idxcall(typename index::generator::type(), forward(Args)...); + return idxcall(typename index::generator::type(), polyfill::forward(Args)...); } template inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { - return f(forward(std::get(storage))..., forward(Args)...); + return f(polyfill::forward(polyfill::get(storage))..., polyfill::forward(Args)...); } ContainerAllocator * get_allocator() { @@ -69,7 +69,7 @@ class CaptureFirst * In this case, however, a smarter allocator would help */ Function & f; - std::tuple storage; + polyfill::tuple storage; }; template @@ -84,11 +84,11 @@ class CaptureLast {} ReturnType operator () (ArgTypes&&... Args) { - return idxcall(typename index::generator::type(), forward(Args)...); + return idxcall(typename index::generator::type(), polyfill::forward(Args)...); } template inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { - return f(forward(Args)..., forward(std::get(storage))...); + return f(polyfill::forward(Args)..., polyfill::forward(polyfill::get(storage))...); } ContainerAllocator * get_allocator() { @@ -97,7 +97,7 @@ class CaptureLast protected: Function & f; - std::tuple storage; + polyfill::tuple storage; }; template struct RemoveFirstArgs {}; @@ -123,7 +123,7 @@ struct RemoveLastArgs { template struct RemoveLastArgs { using type = typename std::conditional< - std::is_same,std::tuple >::value, + std::is_same,polyfill::tuple >::value, Function, typename RemoveLastArgs::type >::type; @@ -135,7 +135,7 @@ Function bind_last(Function && static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); - new(newf) CaptureFP(f,forward(CapturedArgs)...); + new(newf) CaptureFP(f,polyfill::forward(CapturedArgs)...); return Function(static_cast*>(newf)); } diff --git a/core-util/v2/detail/forward.hpp b/core-util/v2/detail/forward.hpp deleted file mode 100644 index 15c1a28..0000000 --- a/core-util/v2/detail/forward.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2015 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef FUNCTIONAL_DETAIL_FORWARD_HPP -#define FUNCTIONAL_DETAIL_FORWARD_HPP - -namespace functional { -namespace detail { -template -T&& forward(typename std::remove_reference::type& a) noexcept -{ - return static_cast(a); -} -template -T&& forward(typename std::remove_reference::type&& a) noexcept -{ - return static_cast(a); -} - -} // namespace detail -} // namespace functional - -#endif // FUNCTIONAL_DETAIL_FORWARD_HPP diff --git a/core-util/v2/detail/functor.hpp b/core-util/v2/detail/functor.hpp index 4cd7971..ea49720 100644 --- a/core-util/v2/detail/functor.hpp +++ b/core-util/v2/detail/functor.hpp @@ -33,7 +33,7 @@ class FunctorContainer : public Function FunctorContainer(const FunctorContainer & f) : f(f.f) {} virtual ReturnType operator () (ArgTypes&&... Args) { - return f(detail::forward(Args)...); + return f(polyfill::forward(Args)...); } virtual ContainerAllocator * get_allocator() { return &Allocator; diff --git a/core-util/v2/detail/member.hpp b/core-util/v2/detail/member.hpp index ea6bd78..a499ee5 100644 --- a/core-util/v2/detail/member.hpp +++ b/core-util/v2/detail/member.hpp @@ -18,6 +18,7 @@ #include "interface.hpp" #include "allocators.hpp" +#include "polyfill.hpp" namespace functional { namespace detail { @@ -32,7 +33,7 @@ class MemberContainer : public FunctionInterface *fp)(forward(Args)...); + return (obj->*fp)(polyfill::forward(Args)...); } ContainerAllocator * get_allocator() { return & MemberFPAllocator; diff --git a/core-util/v2/detail/polyfill.hpp b/core-util/v2/detail/polyfill.hpp new file mode 100644 index 0000000..a405b1a --- /dev/null +++ b/core-util/v2/detail/polyfill.hpp @@ -0,0 +1,133 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CORE_UTIL_V2_DETAIL_POLYFILL_HPP +#define CORE_UTIL_V2_DETAIL_POLYFILL_HPP + +namespace polyfill { +template +T&& forward(typename std::remove_reference::type& a) noexcept +{ + return static_cast(a); +} +template +T&& forward(typename std::remove_reference::type&& a) noexcept +{ + return static_cast(a); +} + +template +struct enable_if_impl {}; + +template +struct enable_if_impl { + typedef T type; +}; + +template +struct enable_if : public enable_if_impl {}; + +template +struct enable_if_c : public enable_if_impl {}; + +template +struct size_gt { + static constexpr bool value = sizeof(T0) > sizeof(T1); +}; +template +struct size_le { + static constexpr bool value = sizeof(T0) <= sizeof(T1); +}; + +template +struct enable_if_size_gt : enable_if::value, T> {}; +template +struct enable_if_size_le : enable_if::value, T> {}; + + + +template +struct tuple_element { + using type = typename tuple_element::type; +}; + +template +struct tuple_element<0,T0,T...> { + using type = T0; +}; + +template +struct tuple_impl; + +template +struct tuple_impl { + T0 t0; + tuple_impl(const T0 &t0) : t0(forward(t0)) {} + template + typename tuple_element::type & get() { + static_assert(I == 0, "tuple range exceeded"); + return t0; + } +}; + +template +struct tuple_impl::type, T0, T1, T...> { + T0 t0; + tuple_impl t; + + tuple_impl(const T0 &t0, const T1 &t1, const T&... t) : + t0(forward(t0)), t(forward(t1), forward(t)...) {} + template + typename enable_if::type>::type & get() { + return t.get(); + } + template + typename enable_if::type>::type & get() { + return t0; + } +}; + +template +struct tuple_impl::type, T0, T1, T...> { + tuple_impl t; + T0 t0; + + tuple_impl(const T0 &t0, const T1 &t1, const T&... t) : + t(forward(t1), forward(t)...), t0(forward(t0)) {} + template + typename enable_if::type>::type & get() { + return t.get(); + } + template + typename enable_if::type>::type & get() { + return t0; + } +}; + + +template +struct tuple : public tuple_impl { + tuple(const T&... t) : tuple_impl(forward(t)...) {} + tuple(){} +}; + +template +constexpr typename tuple_element::type & get(tuple & t) { + return t.get(); +} + +} // namespace polyfill + +#endif // CORE_UTIL_V2_DETAIL_POLYFILL_HPP diff --git a/core-util/v2/detail/static.hpp b/core-util/v2/detail/static.hpp index de5c93c..4f99814 100644 --- a/core-util/v2/detail/static.hpp +++ b/core-util/v2/detail/static.hpp @@ -18,6 +18,7 @@ #include "interface.hpp" #include "allocators.hpp" +#include "polyfill.hpp" namespace functional { namespace detail { @@ -31,7 +32,7 @@ class StaticContainer : public FunctionInterface (Args)...); + return fp(polyfill::forward(Args)...); } virtual ContainerAllocator * get_allocator() { return & StaticFPAllocator; diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 4a42c4a..381ba9e 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -43,7 +43,7 @@ class Function; } // namespace functional -#include "detail/forward.hpp" +#include "detail/polyfill.hpp" #include "detail/interface.hpp" #include "detail/static.hpp" #include "detail/member.hpp" @@ -146,7 +146,7 @@ class Function { } template - Function(std::tuple& t, Function& f) { + Function(polyfill::tuple& t, Function& f) { typedef typename detail::CaptureFirst CaptureFP; static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); @@ -161,7 +161,7 @@ class Function { typename detail::RemoveFirstArgs::type bind_first( CapturedTypes... CapturedArgs) { - std::tuple t(detail::forward(CapturedArgs)...); + polyfill::tuple t(polyfill::forward(CapturedArgs)...); typename detail::RemoveFirstArgs::type f(t,*this); return f; } @@ -171,7 +171,7 @@ class Function { { using ReturnFP = typename detail::RemoveLastArgs::type; static_assert(std::is_same >::value, "oops"); - ReturnFP f(detail::bind_last(ReturnFP(), *this, detail::forward(CapturedArgs)...)); + ReturnFP f(detail::bind_last(ReturnFP(), *this, polyfill::forward(CapturedArgs)...)); return f; } @@ -196,10 +196,10 @@ class Function { } } inline ReturnType operator () (ArgTypes&&... Args) { - return (*ref)(detail::forward(Args)...); + return (*ref)(polyfill::forward(Args)...); } inline ReturnType call(ArgTypes&&... Args) { - return (*ref)(detail::forward(Args)...); + return (*ref)(polyfill::forward(Args)...); } void * get_ref() { if (ref) @@ -217,7 +217,7 @@ class Function { detail::FunctionInterface * sref = reinterpret_cast *>(vref); CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); - return (*sref)(detail::forward(Args)...); + return (*sref)(polyfill::forward(Args)...); } static ReturnType call_from_void_dec(void *vref, ArgTypes... Args) { detail::FunctionInterface * sref = @@ -231,7 +231,7 @@ class Function { detail::FunctionInterface * sref = reinterpret_cast *>(vref); CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); - ReturnType r((*sref)(detail::forward(Args)...)); + ReturnType r((*sref)(polyfill::forward(Args)...)); sref->dec(); return r; } From 37e6d9f8ae5b78381f44860dc60b204ae05a3645 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 3 Feb 2016 12:19:40 +0000 Subject: [PATCH 11/14] Add is_same and conditional to library polyfill --- core-util/v2/detail/capture.hpp | 6 ++--- core-util/v2/detail/polyfill.hpp | 43 +++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp index f19a2bc..99c8a80 100644 --- a/core-util/v2/detail/capture.hpp +++ b/core-util/v2/detail/capture.hpp @@ -109,7 +109,7 @@ struct RemoveFirstArgs { template struct RemoveFirstArgs { - static_assert(std::is_same::value, "Type mismatch in argument removal"); + static_assert(polyfill::is_same::value, "Type mismatch in argument removal"); typedef typename RemoveFirstArgs::type type; }; @@ -122,8 +122,8 @@ struct RemoveLastArgs { template struct RemoveLastArgs { - using type = typename std::conditional< - std::is_same,polyfill::tuple >::value, + using type = typename polyfill::conditional< + polyfill::is_same,polyfill::tuple >::value, Function, typename RemoveLastArgs::type >::type; diff --git a/core-util/v2/detail/polyfill.hpp b/core-util/v2/detail/polyfill.hpp index a405b1a..5820b8b 100644 --- a/core-util/v2/detail/polyfill.hpp +++ b/core-util/v2/detail/polyfill.hpp @@ -17,17 +17,54 @@ #define CORE_UTIL_V2_DETAIL_POLYFILL_HPP namespace polyfill { +template< class T > struct remove_reference {typedef T type;}; +template< class T > struct remove_reference {typedef T type;}; +template< class T > struct remove_reference {typedef T type;}; + template -T&& forward(typename std::remove_reference::type& a) noexcept +typename remove_reference::type&& +move(T&& a) noexcept { - return static_cast(a); + typedef typename remove_reference::type&& RvalRef; + return static_cast(a); +} +template +T&& +forward(typename remove_reference::type& a) noexcept +{ + return static_cast(a); } template -T&& forward(typename std::remove_reference::type&& a) noexcept +T&& +forward(typename remove_reference::type&& a) noexcept { return static_cast(a); } +template +struct is_same { + static constexpr bool value = false; +}; + +template +struct is_same { + static constexpr bool value = true; +}; + +template +struct conditional; + +template +struct conditional { + using type = T0; +}; + +template +struct conditional { + using type = T1; +}; + + template struct enable_if_impl {}; From 70df7d53e657b4f2909abab1a53a2ac9fc228634 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 3 Feb 2016 14:09:41 +0000 Subject: [PATCH 12/14] Fix armcc support --- core-util/v2/detail/capture.hpp | 19 ++++++++++++------- core-util/v2/detail/interface.hpp | 4 ++-- core-util/v2/detail/member.hpp | 2 +- core-util/v2/functional.hpp | 1 - test/Functional/main.cpp | 8 ++++---- 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp index 99c8a80..c204b7c 100644 --- a/core-util/v2/detail/capture.hpp +++ b/core-util/v2/detail/capture.hpp @@ -45,19 +45,24 @@ template : public FunctionInterface { public: - CaptureFirst(const polyfill::tuple& t, Function& f): + using stype = polyfill::tuple; + CaptureFirst(const stype & t, Function& f): f(f), storage(t) {} - ReturnType operator () (ArgTypes&&... Args) { + virtual ReturnType operator () (ArgTypes&&... Args) { return idxcall(typename index::generator::type(), polyfill::forward(Args)...); } template inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { - return f(polyfill::forward(polyfill::get(storage))..., polyfill::forward(Args)...); + return f( + polyfill::forward( + polyfill::get(storage) + )..., + polyfill::forward(Args)...); } - ContainerAllocator * get_allocator() { + virtual ContainerAllocator * get_allocator() { return & Allocator; } protected: @@ -83,15 +88,15 @@ class CaptureLast f(f), storage(CapturedArgs...) {} - ReturnType operator () (ArgTypes&&... Args) { + virtual ReturnType operator () (ArgTypes&&... Args) { return idxcall(typename index::generator::type(), polyfill::forward(Args)...); } template inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { - return f(polyfill::forward(Args)..., polyfill::forward(polyfill::get(storage))...); + return f(polyfill::forward(Args)..., polyfill::forward(polyfill::get(storage))...); } - ContainerAllocator * get_allocator() { + virtual ContainerAllocator * get_allocator() { return & Allocator; } protected: diff --git a/core-util/v2/detail/interface.hpp b/core-util/v2/detail/interface.hpp index 0aa2fb1..67f6dfe 100644 --- a/core-util/v2/detail/interface.hpp +++ b/core-util/v2/detail/interface.hpp @@ -36,11 +36,11 @@ class FunctionInterface { virtual ContainerAllocator *get_allocator() = 0; inline uint32_t inc() { - return mbed::util::atomic_incr(&refcnt, 1ul); + return mbed::util::atomic_incr(&refcnt, static_cast(1)); } inline uint32_t dec() { - return mbed::util::atomic_decr(&refcnt, 1ul); + return mbed::util::atomic_decr(&refcnt, static_cast(1)); } protected: uint32_t refcnt; diff --git a/core-util/v2/detail/member.hpp b/core-util/v2/detail/member.hpp index a499ee5..9e5a9d5 100644 --- a/core-util/v2/detail/member.hpp +++ b/core-util/v2/detail/member.hpp @@ -35,7 +35,7 @@ class MemberContainer : public FunctionInterface *fp)(polyfill::forward(Args)...); } - ContainerAllocator * get_allocator() { + virtual ContainerAllocator * get_allocator() { return & MemberFPAllocator; } diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index 381ba9e..bb05755 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -170,7 +170,6 @@ class Function { typename detail::RemoveLastArgs::type bind_last(CapturedTypes... CapturedArgs) { using ReturnFP = typename detail::RemoveLastArgs::type; - static_assert(std::is_same >::value, "oops"); ReturnFP f(detail::bind_last(ReturnFP(), *this, polyfill::forward(CapturedArgs)...)); return f; } diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 83d7d9c..17d84ac 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -9,16 +9,16 @@ #include "core-util/v2/functional.hpp" void testprnt() { - printf("%s\r\n", __PRETTY_FUNCTION__); + std::printf("%s\r\n", __PRETTY_FUNCTION__); } void testprnt3(int i, int j, int k) { - printf("%s(%i, %i, %i)\r\n", __PRETTY_FUNCTION__, i, j, k); + std::printf("%s(%i, %i, %i)\r\n", __PRETTY_FUNCTION__, i, j, k); } class foo { public: void testprnt() { - printf("%s\r\n", __PRETTY_FUNCTION__); + std::printf("%s\r\n", __PRETTY_FUNCTION__); } }; @@ -32,7 +32,7 @@ void app_start(int , char **) int i = 1; functional::Function f3( [i]() { - printf("%s(%i)\r\n", __PRETTY_FUNCTION__, i); + std::printf("%s(%i)\r\n", __PRETTY_FUNCTION__, i); } ); f3(); From 86c8504e1a4beb7fbafba48661fc59fd1ec49238 Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 3 Feb 2016 15:35:36 +0000 Subject: [PATCH 13/14] Use the alignment intrinsic instead of sizeof for sorting tuples --- core-util/v2/detail/polyfill.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core-util/v2/detail/polyfill.hpp b/core-util/v2/detail/polyfill.hpp index 5820b8b..c12d7c7 100644 --- a/core-util/v2/detail/polyfill.hpp +++ b/core-util/v2/detail/polyfill.hpp @@ -80,18 +80,18 @@ template struct enable_if_c : public enable_if_impl {}; template -struct size_gt { - static constexpr bool value = sizeof(T0) > sizeof(T1); +struct align_gt { + static constexpr bool value = __alignof__(T0) > __alignof__(T1); }; template -struct size_le { - static constexpr bool value = sizeof(T0) <= sizeof(T1); +struct align_le { + static constexpr bool value = __alignof__(T0) <= __alignof__(T1); }; template -struct enable_if_size_gt : enable_if::value, T> {}; +struct enable_if_align_gt : enable_if::value, T> {}; template -struct enable_if_size_le : enable_if::value, T> {}; +struct enable_if_align_le : enable_if::value, T> {}; @@ -120,7 +120,7 @@ struct tuple_impl { }; template -struct tuple_impl::type, T0, T1, T...> { +struct tuple_impl::type, T0, T1, T...> { T0 t0; tuple_impl t; @@ -137,7 +137,7 @@ struct tuple_impl::type, T0, T1, T...> { }; template -struct tuple_impl::type, T0, T1, T...> { +struct tuple_impl::type, T0, T1, T...> { tuple_impl t; T0 t0; From b3ba41162f005439e817de773e9aa6d9f89c4c7b Mon Sep 17 00:00:00 2001 From: Brendan Moran Date: Wed, 17 Feb 2016 15:01:38 +0000 Subject: [PATCH 14/14] Fix the static init fiasco --- core-util/v2/detail/allocators.hpp | 57 +++++++---- core-util/v2/detail/capture.hpp | 26 ++--- core-util/v2/detail/functor.hpp | 14 +-- core-util/v2/detail/interface.hpp | 2 +- core-util/v2/detail/member.hpp | 4 +- core-util/v2/detail/polyfill.hpp | 57 ++++------- core-util/v2/detail/static.hpp | 4 +- core-util/v2/functional.hpp | 155 ++++++++++++++++++----------- source/v2/functional.cpp | 97 ++++++++++++------ test/Functional/main.cpp | 8 ++ 10 files changed, 251 insertions(+), 173 deletions(-) diff --git a/core-util/v2/detail/allocators.hpp b/core-util/v2/detail/allocators.hpp index c3bc9ba..71be6dd 100644 --- a/core-util/v2/detail/allocators.hpp +++ b/core-util/v2/detail/allocators.hpp @@ -17,49 +17,68 @@ #define FUNCTIONAL_DETAIL_ALLOCATORS_HPP #include "interface.hpp" + #include "core-util/ExtendablePoolAllocator.h" #include "ualloc/ualloc.h" +#include namespace functional { namespace detail { class ContainerAllocator : public mbed::util::ExtendablePoolAllocator { public: - ContainerAllocator(size_t initial_elements, size_t new_pool_elements, size_t element_size, - UAllocTraits_t alloc_traits, unsigned alignment = MBED_UTIL_POOL_ALLOC_DEFAULT_ALIGN) - : mbed::util::ExtendablePoolAllocator() - { - this->init(initial_elements, new_pool_elements, element_size, alloc_traits, alignment); - } template - bool free(FunctionInterface * ptr) { + void free(FunctionInterface * ptr) { ptr->~FunctionInterface(); mbed::util::ExtendablePoolAllocator::free(ptr); - return true; } + ContainerAllocator(std::size_t initial_elements, std::size_t new_pool_elements, std::size_t element_size) : + mbed::util::ExtendablePoolAllocator() + { + this->init(initial_elements, new_pool_elements, element_size, UAllocTraits_t{.flags = UALLOC_TRAITS_NEVER_FREE}, + MBED_UTIL_POOL_ALLOC_DEFAULT_ALIGN); + } +}; + +template +class ContainerAllocatorWrapper { +public: + static const std::size_t size = ALLOC_SIZE; + static ContainerAllocator & instance(); }; -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE -#define FUNCTOR_SIZE YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE -#else +namespace alloc_size { +class BaseClass { +public: + virtual void base_function(){} +}; +class VirtualClass : public BaseClass { + virtual void base_function(){} +}; +class UnknownClass; + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE /* - * Optimization note: This size was chosen to allow a Function to bind a DNS response. + * This size was chosen to allow a Function to bind a DNS response. + * 4 bytes for a reference count * 4 bytes for the vtable pointers * 4 bytes for the base Function * 128/8 bytes for an IPv6 address * 4 bytes for a char* pointer. * 4 bytes of padding to round out to 8-byte alignment. - * - * This size should be optimized further by examining application requirements. */ -#define FUNCTOR_SIZE (4 + 4 + (128/8) + 4 + 4) +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE \ + ((sizeof(std::uint32_t) + sizeof(VirtualClass) + sizeof(UnknownClass *) + 128/8 + sizeof(char *) + 7) & ~7) #endif +const std::size_t staticfp = sizeof(std::uint32_t) + sizeof(VirtualClass) + sizeof(void(*)()); +const std::size_t memberfp = sizeof(std::uint32_t) + sizeof(VirtualClass) + sizeof(UnknownClass *) + sizeof(void (UnknownClass::*)()); +const std::size_t functorfp = YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE; +} -extern ContainerAllocator StaticFPAllocator; -extern ContainerAllocator MemberFPAllocator; -extern ContainerAllocator FunctorFPAllocator; - +using StaticFPAllocator = ContainerAllocatorWrapper; +using MemberFPAllocator = ContainerAllocatorWrapper; +using FunctorFPAllocator = ContainerAllocatorWrapper; } // detail } // functional diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp index c204b7c..44bff2f 100644 --- a/core-util/v2/detail/capture.hpp +++ b/core-util/v2/detail/capture.hpp @@ -21,6 +21,8 @@ #include "interface.hpp" #include "allocators.hpp" +#include "core-util/assert.h" + namespace functional { namespace detail { @@ -38,10 +40,10 @@ struct generator<0, S...> { }; } // namespace index -template +template class CaptureFirst; -template +template class CaptureFirst : public FunctionInterface { public: @@ -58,12 +60,12 @@ class CaptureFirst return f( polyfill::forward( polyfill::get(storage) - )..., + )..., polyfill::forward(Args)...); } - virtual ContainerAllocator * get_allocator() { - return & Allocator; + virtual ContainerAllocator & get_allocator() { + return Allocator(); } protected: /* @@ -77,10 +79,10 @@ class CaptureFirst polyfill::tuple storage; }; -template +template class CaptureLast; -template +template class CaptureLast : public FunctionInterface { public: @@ -96,8 +98,8 @@ class CaptureLast return f(polyfill::forward(Args)..., polyfill::forward(polyfill::get(storage))...); } - virtual ContainerAllocator * get_allocator() { - return & Allocator; + virtual ContainerAllocator & get_allocator() { + return Allocator(); } protected: @@ -136,9 +138,9 @@ struct RemoveLastArgs Function bind_last(Function &&, Function& f, CapturedTypes... CapturedArgs) { - using CaptureFP = CaptureLast; - static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); - CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + using CaptureFP = CaptureLast; + static_assert(sizeof(CaptureFP) <= alloc_size::functorfp, "Size of bound arguments is too large" ); + CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator::instance().alloc()); CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) CaptureFP(f,polyfill::forward(CapturedArgs)...); return Function(static_cast*>(newf)); diff --git a/core-util/v2/detail/functor.hpp b/core-util/v2/detail/functor.hpp index ea49720..3c4c253 100644 --- a/core-util/v2/detail/functor.hpp +++ b/core-util/v2/detail/functor.hpp @@ -23,10 +23,10 @@ namespace functional { namespace detail { -template +template class FunctorContainer; -template +template class FunctorContainer : public FunctionInterface { public: FunctorContainer(const F & f) : f(f) {} @@ -35,15 +35,9 @@ class FunctorContainer : public Function virtual ReturnType operator () (ArgTypes&&... Args) { return f(polyfill::forward(Args)...); } - virtual ContainerAllocator * get_allocator() { - return &Allocator; + virtual ContainerAllocator & get_allocator() { + return Allocator(); } - - // virtual void deallocate(FunctionInterface *ptr){ - // (void)ptr; - // this->~FunctionInterface(); - // Allocator.free(this); - // } protected: F f; }; diff --git a/core-util/v2/detail/interface.hpp b/core-util/v2/detail/interface.hpp index 67f6dfe..109b42b 100644 --- a/core-util/v2/detail/interface.hpp +++ b/core-util/v2/detail/interface.hpp @@ -33,7 +33,7 @@ class FunctionInterface { public: FunctionInterface() : refcnt(0) {} virtual ReturnType operator () (ArgTypes&&... Args) = 0; - virtual ContainerAllocator *get_allocator() = 0; + virtual ContainerAllocator & get_allocator() = 0; inline uint32_t inc() { return mbed::util::atomic_incr(&refcnt, static_cast(1)); diff --git a/core-util/v2/detail/member.hpp b/core-util/v2/detail/member.hpp index 9e5a9d5..f2a52c7 100644 --- a/core-util/v2/detail/member.hpp +++ b/core-util/v2/detail/member.hpp @@ -35,8 +35,8 @@ class MemberContainer : public FunctionInterface *fp)(polyfill::forward(Args)...); } - virtual ContainerAllocator * get_allocator() { - return & MemberFPAllocator; + virtual ContainerAllocator & get_allocator() { + return MemberFPAllocator::instance(); } // virtual void deallocate(FunctionInterface *ptr) { diff --git a/core-util/v2/detail/polyfill.hpp b/core-util/v2/detail/polyfill.hpp index c12d7c7..61a4e90 100644 --- a/core-util/v2/detail/polyfill.hpp +++ b/core-util/v2/detail/polyfill.hpp @@ -17,6 +17,8 @@ #define CORE_UTIL_V2_DETAIL_POLYFILL_HPP namespace polyfill { +// From ARM Compiler armcc User Guide { +// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472k/chr1407404265784.html template< class T > struct remove_reference {typedef T type;}; template< class T > struct remove_reference {typedef T type;}; template< class T > struct remove_reference {typedef T type;}; @@ -34,6 +36,9 @@ forward(typename remove_reference::type& a) noexcept { return static_cast(a); } +/// } From ARM Compiler armcc User Guide + + template T&& forward(typename remove_reference::type&& a) noexcept @@ -64,7 +69,6 @@ struct conditional { using type = T1; }; - template struct enable_if_impl {}; @@ -76,9 +80,6 @@ struct enable_if_impl { template struct enable_if : public enable_if_impl {}; -template -struct enable_if_c : public enable_if_impl {}; - template struct align_gt { static constexpr bool value = __alignof__(T0) > __alignof__(T1); @@ -95,7 +96,7 @@ struct enable_if_align_le : enable_if::value, T> {}; -template +template struct tuple_element { using type = typename tuple_element::type; }; @@ -105,62 +106,46 @@ struct tuple_element<0,T0,T...> { using type = T0; }; -template +template struct tuple_impl; template -struct tuple_impl { +struct tuple_impl { T0 t0; tuple_impl(const T0 &t0) : t0(forward(t0)) {} - template + template typename tuple_element::type & get() { static_assert(I == 0, "tuple range exceeded"); return t0; } }; -template -struct tuple_impl::type, T0, T1, T...> { +template +struct tuple_impl { T0 t0; - tuple_impl t; + tuple_impl t; - tuple_impl(const T0 &t0, const T1 &t1, const T&... t) : - t0(forward(t0)), t(forward(t1), forward(t)...) {} - template - typename enable_if::type>::type & get() { + tuple_impl(const T0 &t0, const T&... t) : + t0(forward(t0)), t(forward(t)...) {} + template + typename enable_if::type>::type & get() { return t.get(); } - template - typename enable_if::type>::type & get() { + template + typename enable_if::type>::type & get() { return t0; } }; -template -struct tuple_impl::type, T0, T1, T...> { - tuple_impl t; - T0 t0; - - tuple_impl(const T0 &t0, const T1 &t1, const T&... t) : - t(forward(t1), forward(t)...), t0(forward(t0)) {} - template - typename enable_if::type>::type & get() { - return t.get(); - } - template - typename enable_if::type>::type & get() { - return t0; - } -}; template -struct tuple : public tuple_impl { - tuple(const T&... t) : tuple_impl(forward(t)...) {} +struct tuple : public tuple_impl { + tuple(const T&... t) : tuple_impl(forward(t)...) {} tuple(){} }; -template +template constexpr typename tuple_element::type & get(tuple & t) { return t.get(); } diff --git a/core-util/v2/detail/static.hpp b/core-util/v2/detail/static.hpp index 4f99814..49cadb8 100644 --- a/core-util/v2/detail/static.hpp +++ b/core-util/v2/detail/static.hpp @@ -34,8 +34,8 @@ class StaticContainer : public FunctionInterface (Args)...); } - virtual ContainerAllocator * get_allocator() { - return & StaticFPAllocator; + virtual ContainerAllocator & get_allocator() { + return StaticFPAllocator::instance(); } // virtual void deallocate(FunctionInterface *ptr) { diff --git a/core-util/v2/functional.hpp b/core-util/v2/functional.hpp index bb05755..5769666 100644 --- a/core-util/v2/functional.hpp +++ b/core-util/v2/functional.hpp @@ -26,23 +26,14 @@ * In order to maintain compatibility with existing code, ```Function``` is intended to be duck-type compatible with * the ```FunctionPointer``` family of classes, but it is intentionally not named the same way. A family of * compatibility classes is provided to support conversion from ```FunctionPointer``` code to ```Function```. - * These compatibility classes will be deprecated in an upcoming release. + * These compatibility classes will be deprecated. */ -#include -#include -#include -#include -#include -#include "core-util/atomic_ops.h" -#include "core-util/assert.h" - namespace functional { template class Function; } // namespace functional - #include "detail/polyfill.hpp" #include "detail/interface.hpp" #include "detail/static.hpp" @@ -51,6 +42,15 @@ class Function; #include "detail/allocators.hpp" #include "detail/capture.hpp" +#include "core-util/atomic_ops.h" +#include "core-util/assert.h" + +#include +#include +#include +#include +#include + namespace functional { /** * Superficially, ```Function``` is intented to appear similar to ```std::function```. However, ```Function``` has a @@ -70,48 +70,98 @@ namespace functional { template class Function { public: + using Interface = detail::FunctionInterface; /** * The empty constructor. * Since ```Function``` only contains a single pointer, only a null assignment is necessary. */ - Function() : ref(nullptr) {} + Function() noexcept : ref(nullptr) {} + /** + * Copy constructor + * This constructor copies and increments the reference from the lvalue-reference + * Function without incrementing the reference count. + */ + Function(const Function &f) noexcept : ref(f.ref) { + if(ref) { + ref->inc(); + } + } + /** + * Move constructor + * This constructor steals the reference from the rvalue-reference Function + * without incrementing the reference count. + */ + Function(Function &&f) noexcept : ref(f.ref) {} + + /** + * @brief Constructs a Function that targets the incoming Functor object + * @param[in] f a Functor that is callable + */ + template + Function(const F &f) noexcept : ref(nullptr) { + typedef detail::FunctorContainer FunctorFP; + static_assert(sizeof(FunctorFP) <= detail::FunctorFPAllocator::size, + "Functor size too large for default functor allocator size."); + void * newf = detail::FunctorFPAllocator::instance().alloc(); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); + if (newf) { + ref = new(newf) FunctorFP(f); + ref->inc(); + } + } + + template + void attach(const F & f) { + Function func(f); + *this = func; + } + + /** * Construct a Function from a FunctionInterface. * * This is an API that should only be used by Function and its helpers. It was specifically added to enable - * bind_first and bind_last. + * bind_first and bind_last. Due to template inference, the version of Function that requires this constructor + * is not the same as the version that instantiates it, so this cannot be a protected API. * - * @param[in] f + * @param[in] f the FunctionInterface to reference in this Function */ - Function(detail::FunctionInterface *f) { + Function(Interface *f) { ref = f; if (ref) { ref->inc(); } } - /** - * Move constructor - * This constructor steals the reference from the rvalue-reference Function - * without incrementing the reference count. - */ - Function(Function &&f): ref(f.ref) {} + /** Create a Function, attaching a static function + * + * @param f The static function to attach + */ Function(ReturnType (*f)(ArgTypes...)) { typedef detail::StaticContainer staticFP; - staticFP * newf = reinterpret_cast(detail::StaticFPAllocator.alloc()); + staticFP * newf = reinterpret_cast(detail::StaticFPAllocator::instance().alloc()); CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) staticFP(f); ref = newf; ref->inc(); } + /** Attach a static function + * + * @param f The static function to attach (default is none) + */ void attach(ReturnType (*f)(ArgTypes...)) { Function func(f); *this = func; } + /** Attach a member function + * + * @param o The object pointer to invoke the member function on (i.e. the this pointer) + * @param f The address of the member function to attach + */ template Function(C *o, ReturnType (C::*fp)(ArgTypes...)) { typedef detail::MemberContainer memberFP; - memberFP * newf = reinterpret_cast(detail::MemberFPAllocator.alloc()); + memberFP * newf = reinterpret_cast(detail::MemberFPAllocator::instance().alloc()); CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) memberFP(o, fp); ref = newf; @@ -123,33 +173,11 @@ class Function { *this = func; } - - //Optimization Note: use template metaprogramming to select one of three allocator pools - template - Function(const F &f) { - typedef detail::FunctorContainer FunctorFP; - FunctorFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); - CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); - new(newf) FunctorFP(f); - ref = newf; - ref->inc(); - } - template - void attach(const F & f) { - Function func(f); - *this = func; - } - Function(const Function & f): ref(f.ref) { - if(ref) { - ref->inc(); - } - } - template Function(polyfill::tuple& t, Function& f) { - typedef typename detail::CaptureFirst CaptureFP; - static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" ); - CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator.alloc()); + typedef typename detail::CaptureFirst CaptureFP; + static_assert(sizeof(CaptureFP) <= detail::alloc_size::functorfp, "Size of bound arguments is too large" ); + CaptureFP * newf = reinterpret_cast(detail::FunctorFPAllocator::instance().alloc()); CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); new(newf) CaptureFP(t, f); ref = newf; @@ -159,15 +187,19 @@ class Function { // Optimization note: This should be changed to use the same constructor as bind_last. template typename detail::RemoveFirstArgs::type bind_first( - CapturedTypes... CapturedArgs) + CapturedTypes&&... CapturedArgs) { polyfill::tuple t(polyfill::forward(CapturedArgs)...); typename detail::RemoveFirstArgs::type f(t,*this); return f; } + Function bind(ArgTypes&&... Args) { + return bind_last(polyfill::forward(Args)...); + } + template - typename detail::RemoveLastArgs::type bind_last(CapturedTypes... CapturedArgs) + typename detail::RemoveLastArgs::type bind_last(CapturedTypes&&... CapturedArgs) { using ReturnFP = typename detail::RemoveLastArgs::type; ReturnFP f(detail::bind_last(ReturnFP(), *this, polyfill::forward(CapturedArgs)...)); @@ -178,7 +210,7 @@ class Function { { if (ref) { if (ref->dec() == 0) { - ref->get_allocator()->free(ref); + ref->get_allocator().free(ref); } } } @@ -186,7 +218,7 @@ class Function { { if (ref) { if (ref->dec() == 0) { - ref->get_allocator()->free(ref); + ref->get_allocator().free(ref); } } ref = rhs.ref; @@ -200,35 +232,40 @@ class Function { inline ReturnType call(ArgTypes&&... Args) { return (*ref)(polyfill::forward(Args)...); } + void clear() { + if (ref) { + ref->dec(); + } + ref = NULL; + } void * get_ref() { if (ref) ref->inc(); return reinterpret_cast(ref); } + void drop_ref(void * ref) { + reinterpret_cast(ref)->dec(); + } /* Note that this function does not use rvalue references beceause it is for C API interoperability */ static ReturnType call_from_void(void *vref, ArgTypes... Args) { - detail::FunctionInterface * sref = - reinterpret_cast *>(vref); + Interface * sref = reinterpret_cast(vref); CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); return (*sref)(Args...); } static ReturnType call_from_void_rref(void *vref, ArgTypes&&... Args) { - detail::FunctionInterface * sref = - reinterpret_cast *>(vref); + Interface * sref = reinterpret_cast(vref); CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); return (*sref)(polyfill::forward(Args)...); } static ReturnType call_from_void_dec(void *vref, ArgTypes... Args) { - detail::FunctionInterface * sref = - reinterpret_cast *>(vref); + Interface * sref = reinterpret_cast(vref); CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); ReturnType r((*sref)(Args...)); sref->dec(); return r; } static ReturnType call_from_void_dec_rref(void *vref, ArgTypes&&... Args) { - detail::FunctionInterface * sref = - reinterpret_cast *>(vref); + Interface * sref = reinterpret_cast(vref); CORE_UTIL_ASSERT_MSG(sref != NULL, "Cannot call a null Function object"); ReturnType r((*sref)(polyfill::forward(Args)...)); sref->dec(); diff --git a/source/v2/functional.cpp b/source/v2/functional.cpp index 05b8214..ef9889c 100644 --- a/source/v2/functional.cpp +++ b/source/v2/functional.cpp @@ -17,56 +17,89 @@ #include "core-util/v2/functional.hpp" -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL -#define STATICFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL -#else -#define STATICFP_INITIAL 8 +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL 4 #endif -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY -#define STATICFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY -#else -#define STATICFP_GROWBY STATICFP_INITIAL +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY 4 #endif -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL -#define MEMBERFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL -#else -#define MEMBERFP_INITIAL 8 + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL +#endif + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY +#endif + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL #endif -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY -#define MEMBERFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY -#else -#define MEMBERFP_GROWBY MEMBERFP_INITIAL +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY #endif -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL -#define FUNCTORFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL -#else -#define FUNCTORFP_INITIAL 8 +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL #endif -#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_GROWBY -#define FUNCTORFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_GROWBY -#else -#define FUNCTORFP_GROWBY FUNCTORFP_INITIAL +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_GROWBY +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY #endif namespace functional { namespace detail { -const UAllocTraits_t NeverFreeTrait = {.flags = UALLOC_TRAITS_NEVER_FREE}; -class UnknownClass; +template +ContainerAllocator & ContainerAllocatorWrapper::instance() +{ + static ContainerAllocator instance( + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL, + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY, + ALLOC_SIZE + ); + return instance; +} -ContainerAllocator StaticFPAllocator(STATICFP_INITIAL, - STATICFP_GROWBY, sizeof(StaticContainer), NeverFreeTrait); +template <> +ContainerAllocator & ContainerAllocatorWrapper::instance() +{ + static_assert(alloc_size::staticfp == sizeof(StaticContainer), "alloc_size::staticfp mismatch"); + static ContainerAllocator instance( + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_INITIAL, + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_STATICFP_GROWBY, + alloc_size::staticfp + ); + return instance; +} + +class UnknownClass; -ContainerAllocator MemberFPAllocator(MEMBERFP_INITIAL, - MEMBERFP_GROWBY, sizeof(MemberContainer), NeverFreeTrait); +template <> +ContainerAllocator & ContainerAllocatorWrapper::instance() +{ + static_assert(alloc_size::memberfp == sizeof(MemberContainer), "alloc_size::staticfp mismatch"); + static ContainerAllocator instance( + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL, + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY, + alloc_size::memberfp + ); + return instance; +} -ContainerAllocator FunctorFPAllocator(FUNCTORFP_INITIAL, - FUNCTORFP_GROWBY, FUNCTOR_SIZE, NeverFreeTrait); +template <> +ContainerAllocator & ContainerAllocatorWrapper::instance() +{ + static ContainerAllocator instance( + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_INITIAL, + YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY, + alloc_size::functorfp + ); + return instance; +} } // namespace detail } // namespace functional diff --git a/test/Functional/main.cpp b/test/Functional/main.cpp index 17d84ac..90aa49b 100644 --- a/test/Functional/main.cpp +++ b/test/Functional/main.cpp @@ -24,6 +24,7 @@ class foo { void app_start(int , char **) { + get_stdio_serial().baud(115200); functional::Function f(testprnt); f(); foo o; @@ -46,4 +47,11 @@ void app_start(int , char **) f7(); functional::Function f8 = f4.bind_last(4); f8(1,2); + + struct s0 { char c; int i;}; + struct s1 { int i; char c;}; + + std::printf("alignof(struct {char, int}) = %u\r\n", __alignof__(struct s0)); + std::printf("alignof(struct {int, char}) = %u\r\n", __alignof__(struct s1)); + }