diff --git a/core-util/v2/detail/allocators.hpp b/core-util/v2/detail/allocators.hpp new file mode 100644 index 0000000..71be6dd --- /dev/null +++ b/core-util/v2/detail/allocators.hpp @@ -0,0 +1,85 @@ +/* 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" + +#include +namespace functional { +namespace detail { + +class ContainerAllocator : public mbed::util::ExtendablePoolAllocator { +public: + template + void free(FunctionInterface * ptr) { + ptr->~FunctionInterface(); + mbed::util::ExtendablePoolAllocator::free(ptr); + } + 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(); +}; + +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 +/* + * 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. + */ +#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; +} + +using StaticFPAllocator = ContainerAllocatorWrapper; +using MemberFPAllocator = ContainerAllocatorWrapper; +using FunctorFPAllocator = ContainerAllocatorWrapper; + +} // detail +} // functional +#endif // FUNCTIONAL_DETAIL_ALLOCATORS_HPP diff --git a/core-util/v2/detail/capture.hpp b/core-util/v2/detail/capture.hpp new file mode 100644 index 0000000..44bff2f --- /dev/null +++ b/core-util/v2/detail/capture.hpp @@ -0,0 +1,152 @@ +/* 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 "polyfill.hpp" + +#include "interface.hpp" +#include "allocators.hpp" + +#include "core-util/assert.h" + +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 CaptureFirst; + +template +class CaptureFirst + : public FunctionInterface { +public: + using stype = polyfill::tuple; + CaptureFirst(const stype & t, Function& f): + f(f), storage(t) + {} + + 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)...); + } + + virtual ContainerAllocator & get_allocator() { + return Allocator(); + } +protected: + /* + * Future Optimization Note: It is possible to reduce memory consumption and call + * 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 + */ + Function & f; + polyfill::tuple storage; +}; + +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(), polyfill::forward(Args)...); + } + template + inline ReturnType idxcall(index::sequence, ArgTypes&&... Args) { + return f(polyfill::forward(Args)..., polyfill::forward(polyfill::get(storage))...); + } + + virtual ContainerAllocator & get_allocator() { + return Allocator(); + } +protected: + + Function & f; + polyfill::tuple storage; +}; + +template struct RemoveFirstArgs {}; + +template +struct RemoveFirstArgs { + typedef Function type; +}; + +template +struct RemoveFirstArgs { + static_assert(polyfill::is_same::value, "Type mismatch in argument removal"); + typedef typename RemoveFirstArgs::type type; +}; + +template struct RemoveLastArgs; + +template +struct RemoveLastArgs { + using type = void; +}; + +template +struct RemoveLastArgs { + using type = typename polyfill::conditional< + polyfill::is_same,polyfill::tuple >::value, + Function, + typename RemoveLastArgs::type + >::type; +}; + +template +Function bind_last(Function &&, Function& f, CapturedTypes... CapturedArgs) { + 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)); +} + + +} // 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 new file mode 100644 index 0000000..3c4c253 --- /dev/null +++ b/core-util/v2/detail/functor.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_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(polyfill::forward(Args)...); + } + virtual ContainerAllocator & get_allocator() { + return Allocator(); + } +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..109b42b --- /dev/null +++ b/core-util/v2/detail/interface.hpp @@ -0,0 +1,52 @@ +/* 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 "core-util/atomic_ops.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; + virtual ContainerAllocator & get_allocator() = 0; + inline uint32_t inc() + { + return mbed::util::atomic_incr(&refcnt, static_cast(1)); + } + inline uint32_t dec() + { + return mbed::util::atomic_decr(&refcnt, static_cast(1)); + } +protected: + 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..f2a52c7 --- /dev/null +++ b/core-util/v2/detail/member.hpp @@ -0,0 +1,65 @@ +/* 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" +#include "polyfill.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)(polyfill::forward(Args)...); + } + virtual ContainerAllocator & get_allocator() { + return MemberFPAllocator::instance(); + } + + // 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/polyfill.hpp b/core-util/v2/detail/polyfill.hpp new file mode 100644 index 0000000..61a4e90 --- /dev/null +++ b/core-util/v2/detail/polyfill.hpp @@ -0,0 +1,155 @@ +/* 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 { +// 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;}; + +template +typename remove_reference::type&& +move(T&& a) noexcept +{ + typedef typename remove_reference::type&& RvalRef; + return static_cast(a); +} +template +T&& +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 +{ + 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 {}; + +template +struct enable_if_impl { + typedef T type; +}; + +template +struct enable_if : public enable_if_impl {}; + +template +struct align_gt { + static constexpr bool value = __alignof__(T0) > __alignof__(T1); +}; +template +struct align_le { + static constexpr bool value = __alignof__(T0) <= __alignof__(T1); +}; + +template +struct enable_if_align_gt : enable_if::value, T> {}; +template +struct enable_if_align_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 { + T0 t0; + tuple_impl t; + + 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() { + 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 new file mode 100644 index 0000000..49cadb8 --- /dev/null +++ b/core-util/v2/detail/static.hpp @@ -0,0 +1,50 @@ +/* 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" +#include "polyfill.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(polyfill::forward(Args)...); + } + virtual ContainerAllocator & get_allocator() { + return StaticFPAllocator::instance(); + } + + // 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..5769666 --- /dev/null +++ b/core-util/v2/functional.hpp @@ -0,0 +1,301 @@ +/* mbed Microcontroller Library + * 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. + * 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 a ```std::function```-like interface for containing reference to callables. + * + * 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. + */ + +namespace functional { +template +class Function; + +} // namespace functional +#include "detail/polyfill.hpp" +#include "detail/interface.hpp" +#include "detail/static.hpp" +#include "detail/member.hpp" +#include "detail/functor.hpp" +#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 + * 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: + using Interface = detail::FunctionInterface; + /** + * The empty constructor. + * Since ```Function``` only contains a single pointer, only a null assignment is necessary. + */ + 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. 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 the FunctionInterface to reference in this Function + */ + Function(Interface *f) { + ref = f; + if (ref) { + ref->inc(); + } + } + + /** 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::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::instance().alloc()); + CORE_UTIL_ASSERT_MSG(newf, "Function container memory allocation failed"); + new(newf) memberFP(o, fp); + ref = newf; + ref->inc(); + } + template + void attach(C *o, ReturnType (C::*fp)(ArgTypes...)) { + Function func(o,fp); + *this = func; + } + + template + Function(polyfill::tuple& t, Function& f) { + 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; + ref->inc(); + } + + // Optimization note: This should be changed to use the same constructor as bind_last. + template + typename detail::RemoveFirstArgs::type bind_first( + 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) + { + using ReturnFP = typename detail::RemoveLastArgs::type; + ReturnFP f(detail::bind_last(ReturnFP(), *this, polyfill::forward(CapturedArgs)...)); + return f; + } + + ~Function() + { + if (ref) { + if (ref->dec() == 0) { + ref->get_allocator().free(ref); + } + } + } + Function & operator = (const Function & rhs) + { + if (ref) { + if (ref->dec() == 0) { + ref->get_allocator().free(ref); + } + } + ref = rhs.ref; + if(ref) { + ref->inc(); + } + } + inline ReturnType operator () (ArgTypes&&... Args) { + return (*ref)(polyfill::forward(Args)...); + } + 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) { + 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) { + 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) { + 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) { + 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(); + return r; + } + +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/source/v2/functional.cpp b/source/v2/functional.cpp new file mode 100644 index 0000000..ef9889c --- /dev/null +++ b/source/v2/functional.cpp @@ -0,0 +1,105 @@ +/* + * 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" + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL 4 +#endif + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY 4 +#endif + + +#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 + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_MEMBERFP_GROWBY YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_GROWBY +#endif + +#ifndef YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL +#define YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTORFP_INITIAL YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_DEFAULT_INITIAL +#endif + +#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 { + +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; +} + +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; + +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; +} + +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 new file mode 100644 index 0000000..90aa49b --- /dev/null +++ b/test/Functional/main.cpp @@ -0,0 +1,57 @@ +/* + * 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" + +void testprnt() { + std::printf("%s\r\n", __PRETTY_FUNCTION__); +} +void testprnt3(int i, int j, int k) { + std::printf("%s(%i, %i, %i)\r\n", __PRETTY_FUNCTION__, i, j, k); +} + +class foo { +public: + void testprnt() { + std::printf("%s\r\n", __PRETTY_FUNCTION__); + } +}; + +void app_start(int , char **) +{ + get_stdio_serial().baud(115200); + functional::Function f(testprnt); + f(); + foo o; + functional::Function f2(&o,&foo::testprnt); + f2(); + int i = 1; + functional::Function f3( + [i]() { + std::printf("%s(%i)\r\n", __PRETTY_FUNCTION__, i); + } + ); + f3(); + functional::Function f4(testprnt3); + f4(1,2,3); + functional::Function f5 = f4.bind_first(4); + f5(1,2); + functional::Function f6 = f4.bind_first(4,5); + f6(1); + functional::Function f7 = f4.bind_first(4,5,6); + 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)); + +}