Skip to content
This repository was archived by the owner on Jan 15, 2021. It is now read-only.

Commit 4a1560c

Browse files
committed
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
1 parent 62ca9ee commit 4a1560c

File tree

9 files changed

+172
-62
lines changed

9 files changed

+172
-62
lines changed

core-util/v2/detail/allocators.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,23 @@ class ContainerAllocator : public mbed::util::ExtendablePoolAllocator {
3838
}
3939
};
4040

41+
#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE
42+
#define FUNCTOR_SIZE YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE
43+
#else
44+
/*
45+
* Optimization note: This size was chosen to allow a Function to bind a DNS response.
46+
* 4 bytes for the vtable pointers
47+
* 4 bytes for the base Function
48+
* 128/8 bytes for an IPv6 address
49+
* 4 bytes for a char* pointer.
50+
* 4 bytes of padding to round out to 8-byte alignment.
51+
*
52+
* This size should be optimized further by examining application requirements.
53+
*/
54+
#define FUNCTOR_SIZE (4 + 4 + (128/8) + 4 + 4)
55+
#endif
56+
57+
4158
extern ContainerAllocator StaticFPAllocator;
4259
extern ContainerAllocator MemberFPAllocator;
4360
extern ContainerAllocator FunctorFPAllocator;

core-util/v2/detail/capture.hpp

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,13 @@ struct generator<0, S...> {
4040
} // namespace index
4141

4242
template <typename FunctionType, ContainerAllocator & Allocator, typename... CapturedTypes>
43-
class CapturedArguments;
43+
class CaptureFirst;
4444

4545
template <typename ReturnType, typename... ArgTypes, ContainerAllocator & Allocator, typename... CapturedTypes>
46-
class CapturedArguments <ReturnType(ArgTypes...), Allocator, CapturedTypes...>
46+
class CaptureFirst <ReturnType(ArgTypes...), Allocator, CapturedTypes...>
4747
: public FunctionInterface <ReturnType(ArgTypes...)> {
4848
public:
49-
// CapturedArguments(Function<ReturnType(CapturedTypes..., ArgTypes...)> & f, CapturedTypes&&... CapturedArgs) :
50-
// f(f), storage(CapturedArgs...)
51-
// {}
52-
CapturedArguments(const std::tuple<CapturedTypes...>& t, Function<ReturnType(CapturedTypes..., ArgTypes...)>& f):
49+
CaptureFirst(const std::tuple<CapturedTypes...>& t, Function<ReturnType(CapturedTypes..., ArgTypes...)>& f):
5350
f(f), storage(t)
5451
{}
5552

@@ -61,13 +58,13 @@ class CapturedArguments <ReturnType(ArgTypes...), Allocator, CapturedTypes...>
6158
return f(forward<CapturedTypes>(std::get<S>(storage))..., forward<ArgTypes>(Args)...);
6259
}
6360

64-
virtual ContainerAllocator * getAllocator() {
61+
virtual ContainerAllocator * get_allocator() {
6562
return & Allocator;
6663
}
6764
protected:
6865
/*
6966
* Future Optimization Note: It is possible to reduce memory consumption and call
70-
* overhead by making CapturedArguments inherit from each of the FunctionInterface
67+
* overhead by making CaptureFirst inherit from each of the FunctionInterface
7168
* types, rather than just from FunctionInterface.
7269
*
7370
* In this case, however, a smarter allocator would help
@@ -76,19 +73,73 @@ class CapturedArguments <ReturnType(ArgTypes...), Allocator, CapturedTypes...>
7673
std::tuple<CapturedTypes...> storage;
7774
};
7875

79-
template <typename FunctionType, typename... ToRemove> struct RemoveArgs {};
76+
template <typename FunctionType, ContainerAllocator & Allocator, typename... CapturedTypes>
77+
class CaptureLast;
78+
79+
template <typename ReturnType, typename... ArgTypes, ContainerAllocator & Allocator, typename... CapturedTypes>
80+
class CaptureLast <ReturnType(ArgTypes...), Allocator, CapturedTypes...>
81+
: public FunctionInterface <ReturnType(ArgTypes...)> {
82+
public:
83+
CaptureLast(Function<ReturnType(ArgTypes..., CapturedTypes...)> & f, CapturedTypes&&... CapturedArgs) :
84+
f(f), storage(CapturedArgs...)
85+
{}
86+
87+
virtual ReturnType operator () (ArgTypes&&... Args) {
88+
return idxcall(typename index::generator<sizeof...(CapturedTypes)>::type(), forward<ArgTypes>(Args)...);
89+
}
90+
template <size_t... S>
91+
inline ReturnType idxcall(index::sequence<S...>, ArgTypes&&... Args) {
92+
return f(forward<ArgTypes>(Args)..., forward<CapturedTypes>(std::get<S>(storage))...);
93+
}
94+
95+
virtual ContainerAllocator * get_allocator() {
96+
return & Allocator;
97+
}
98+
protected:
99+
100+
Function<ReturnType(ArgTypes..., CapturedTypes...)> & f;
101+
std::tuple<CapturedTypes...> storage;
102+
};
103+
104+
template <typename FunctionType, typename... ToRemove> struct RemoveFirstArgs {};
80105

81106
template <typename ReturnType, typename... ArgTypes>
82-
struct RemoveArgs <ReturnType(ArgTypes...)> {
107+
struct RemoveFirstArgs <ReturnType(ArgTypes...)> {
83108
typedef Function<ReturnType(ArgTypes...)> type;
84109
};
85110

86111
template <typename ReturnType, typename RemovedArg, typename... ArgTypes, typename ToRemove0, typename... ToRemove>
87-
struct RemoveArgs <ReturnType(RemovedArg, ArgTypes...), ToRemove0, ToRemove...> {
112+
struct RemoveFirstArgs <ReturnType(RemovedArg, ArgTypes...), ToRemove0, ToRemove...> {
88113
static_assert(std::is_same<RemovedArg, ToRemove0>::value, "Type mismatch in argument removal");
89-
typedef typename RemoveArgs<ReturnType(ArgTypes...), ToRemove...>::type type;
114+
typedef typename RemoveFirstArgs<ReturnType(ArgTypes...), ToRemove...>::type type;
90115
};
91116

117+
template <typename FunctionType0, typename FunctionType1, typename... RemoveTypes> struct RemoveLastArgs;
118+
119+
template <typename ReturnType, typename... Types, typename... RemoveTypes>
120+
struct RemoveLastArgs <ReturnType(Types...), ReturnType(), RemoveTypes...> {
121+
using type = void;
122+
};
123+
124+
template <typename ReturnType, typename... Types0, typename Transfer1, typename... Types1, typename... RemoveTypes>
125+
struct RemoveLastArgs <ReturnType(Types0...), ReturnType(Transfer1, Types1...), RemoveTypes...> {
126+
using type = typename std::conditional<
127+
std::is_same<std::tuple<Types1...>,std::tuple<RemoveTypes...> >::value,
128+
Function<ReturnType(Types0..., Transfer1)>,
129+
typename RemoveLastArgs<ReturnType(Types0..., Transfer1), ReturnType(Types1...), RemoveTypes...>::type
130+
>::type;
131+
};
132+
133+
template <typename ReturnType, typename... ArgTypes, typename... ParentTypes, typename... CapturedTypes>
134+
Function<ReturnType(ArgTypes...)> bind_last(Function<ReturnType(ArgTypes...)> &&, Function<ReturnType(ParentTypes...)>& f, CapturedTypes... CapturedArgs) {
135+
using CaptureFP = CaptureLast<ReturnType(ArgTypes...), FunctorFPAllocator, CapturedTypes...>;
136+
static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" );
137+
CaptureFP * newf = reinterpret_cast<CaptureFP *>(detail::FunctorFPAllocator.alloc());
138+
new(newf) CaptureFP(f,forward<CapturedTypes>(CapturedArgs)...);
139+
return Function<ReturnType(ArgTypes...)>(static_cast<FunctionInterface<ReturnType(ArgTypes...)>*>(newf));
140+
}
141+
142+
92143
} // namespace detail
93144
} // namespace functional
94145
#endif // FUNCTIONAL_DETAIL_CAPTURE_HPP

core-util/v2/detail/functor.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class FunctorContainer <F, ReturnType(ArgTypes...), Allocator> : public Function
3535
virtual ReturnType operator () (ArgTypes&&... Args) {
3636
return f(detail::forward<ArgTypes>(Args)...);
3737
}
38-
virtual ContainerAllocator * getAllocator() {
38+
virtual ContainerAllocator * get_allocator() {
3939
return &Allocator;
4040
}
4141

core-util/v2/detail/interface.hpp

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#ifndef FUNCTIONAL_V2_DETAIL_INTERFACE_HPP
1717
#define FUNCTIONAL_V2_DETAIL_INTERFACE_HPP
1818

19-
#include "cmsis.h"
19+
#include "core-util/atomic_ops.h"
2020
namespace functional {
2121
namespace detail {
2222
template <typename FunctionType>
@@ -33,23 +33,17 @@ class FunctionInterface <ReturnType(ArgTypes...)> {
3333
public:
3434
FunctionInterface() : refcnt(0) {}
3535
virtual ReturnType operator () (ArgTypes&&... Args) = 0;
36-
bool inc() {
37-
uint32_t tmp;
38-
do {
39-
tmp = __LDREXW(&refcnt) + 1;
40-
} while (__STREXW(tmp, &refcnt));
41-
return true;
36+
virtual ContainerAllocator *get_allocator() = 0;
37+
inline uint32_t inc()
38+
{
39+
return mbed::util::atomic_incr(&refcnt, 1ul);
4240
}
43-
bool dec() {
44-
uint32_t tmp;
45-
do {
46-
tmp = __LDREXW(&refcnt) - 1;
47-
} while (__STREXW(tmp, &refcnt));
48-
return tmp == 0;
41+
inline uint32_t dec()
42+
{
43+
return mbed::util::atomic_decr(&refcnt, 1ul);
4944
}
50-
virtual ContainerAllocator *getAllocator() = 0;
5145
protected:
52-
volatile uint32_t refcnt;
46+
uint32_t refcnt;
5347
};
5448

5549
} // namespace detail

core-util/v2/detail/member.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class MemberContainer <C, ReturnType(ArgTypes...)> : public FunctionInterface <R
3434
virtual ReturnType operator () (ArgTypes&&... Args) {
3535
return (obj->*fp)(forward<ArgTypes>(Args)...);
3636
}
37-
ContainerAllocator * getAllocator() {
37+
ContainerAllocator * get_allocator() {
3838
return & MemberFPAllocator;
3939
}
4040

core-util/v2/detail/static.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class StaticContainer <ReturnType(ArgTypes...)> : public FunctionInterface <Retu
3333
virtual ReturnType operator () (ArgTypes&&... Args) {
3434
return fp(forward<ArgTypes>(Args)...);
3535
}
36-
virtual ContainerAllocator * getAllocator() {
36+
virtual ContainerAllocator * get_allocator() {
3737
return & StaticFPAllocator;
3838
}
3939

core-util/v2/functional.hpp

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* mbed Microcontroller Library
2-
* Copyright (c) 2006-2015 ARM Limited
2+
* Copyright (c) 2006-2016 ARM Limited
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,15 +18,15 @@
1818

1919
/**
2020
* \file functional.hpp
21-
* \brief A library that provides function containers, allocated by arbitrary allocators
21+
* \brief A library that provides a ```std::function```-like interface for containing reference to callables.
2222
*
23-
* # Function
24-
* Function is the primary API for functional.hpp
25-
* Function derives from a smart pointer to a functor.
26-
* Function contains almost entirely inline APIs
27-
*
28-
* Functor is a callable class.
23+
* The primary API for containing and referring to callables is ```Function```. APIs in the ```detail``` namespace
24+
* should not be invoked directly.
2925
*
26+
* In order to maintain compatibility with existing code, ```Function``` is intended to be duck-type compatible with
27+
* the ```FunctionPointer``` family of classes, but it is intentionally not named the same way. A family of
28+
* compatibility classes is provided to support conversion from ```FunctionPointer``` code to ```Function```.
29+
* These compatibility classes will be deprecated in an upcoming release.
3030
*/
3131

3232
#include <string.h>
@@ -35,7 +35,7 @@
3535
#include <new>
3636
#include <utility>
3737
#include <type_traits>
38-
38+
#include "core-util/atomic_ops.h"
3939

4040
namespace functional {
4141
template <typename FunctionType>
@@ -52,11 +52,36 @@ class Function;
5252
#include "detail/capture.hpp"
5353

5454
namespace functional {
55-
55+
/**
56+
* Superficially, ```Function``` is intented to appear similar to ```std::function```. However, ```Function``` has a
57+
* number of key differences. ```Function``` uses reference counting to limit the copying of large callable objects,
58+
* such as lambdas with large capture lists or ```Function```s with large or many arguments. ```Function``` also uses
59+
* pool allocation so that objects can be created in interrupt context without using ```malloc()```. These choices are
60+
* to overcome two specific limitations of ```std::function```
61+
*
62+
* 1. Copy-constructing a functor requires copy-constructing its member objects. In the case of lambdas, this means
63+
* copy-constructing the lambda captures. lambda captures are not guaranteed to have reentrant copy constructors, so
64+
* lambdas cannot be copy-constructed in interrupt context. Therefore, functors must be created in dynamic memory and
65+
* only pointers to functors can be used.
66+
* 2. Due to 1. creating a functor requires dynamic memory allocation, however malloc is not permitted in interrupt
67+
* context. As a result functors must be pool allocated.
68+
* 3. In order to simplify memory management of functors and due to 1. and 2., functors are reference-counted.
69+
*/
5670
template <typename ReturnType, typename... ArgTypes>
5771
class Function <ReturnType(ArgTypes...)> {
5872
public:
73+
/**
74+
* The empty constructor.
75+
* Since ```Function``` only contains a single pointer, only a null assignment is necessary.
76+
*/
5977
Function() : ref(nullptr) {}
78+
Function(detail::FunctionInterface<ReturnType(ArgTypes...)> *f) {
79+
ref = f;
80+
if (ref) {
81+
ref->inc();
82+
}
83+
}
84+
6085
Function(ReturnType (*f)(ArgTypes...)) {
6186
typedef detail::StaticContainer<ReturnType(ArgTypes...)> staticFP;
6287
staticFP * newf = reinterpret_cast<staticFP *>(detail::StaticFPAllocator.alloc());
@@ -82,6 +107,7 @@ class Function <ReturnType(ArgTypes...)> {
82107
*this = func;
83108
}
84109

110+
85111
//Optimization Note: use template metaprogramming to select one of three allocator pools
86112
template <typename F>
87113
Function(const F &f) {
@@ -97,33 +123,59 @@ class Function <ReturnType(ArgTypes...)> {
97123
*this = func;
98124
}
99125
Function(const Function & f): ref(f.ref) {
100-
ref && ref->inc();
126+
if(ref) {
127+
ref->inc();
128+
}
101129
}
102130

103131
template <typename... CapturedTypes, typename... ParentArgTypes>
104132
Function(std::tuple<CapturedTypes...>& t, Function<ReturnType(ParentArgTypes...)>& f) {
105-
typedef typename detail::CapturedArguments<ReturnType(ArgTypes...), detail::FunctorFPAllocator, CapturedTypes...> CaptureFP;
106-
static_assert(sizeof(CaptureFP) <= 40, "Size of bound arguments is too large" );
133+
typedef typename detail::CaptureFirst<ReturnType(ArgTypes...), detail::FunctorFPAllocator, CapturedTypes...> CaptureFP;
134+
static_assert(sizeof(CaptureFP) <= FUNCTOR_SIZE, "Size of bound arguments is too large" );
107135
CaptureFP * newf = reinterpret_cast<CaptureFP *>(detail::FunctorFPAllocator.alloc());
108136
new(newf) CaptureFP(t, f);
109137
ref = newf;
110138
ref->inc();
111139
}
112140

141+
// Optimization note: This should be changed to use the same constructor as bind_last.
113142
template<typename... CapturedTypes>
114-
typename detail::RemoveArgs<ReturnType(ArgTypes...), CapturedTypes...>::type bind(CapturedTypes... CapturedArgs) {
143+
typename detail::RemoveFirstArgs<ReturnType(ArgTypes...), CapturedTypes...>::type bind_first(
144+
CapturedTypes... CapturedArgs)
145+
{
115146
std::tuple<CapturedTypes...> t(detail::forward<CapturedTypes>(CapturedArgs)...);
116-
typename detail::RemoveArgs<ReturnType(ArgTypes...), CapturedTypes...>::type f(t,*this);
147+
typename detail::RemoveFirstArgs<ReturnType(ArgTypes...), CapturedTypes...>::type f(t,*this);
148+
return f;
149+
}
150+
151+
template<typename... CapturedTypes>
152+
typename detail::RemoveLastArgs<ReturnType(),ReturnType(ArgTypes...),CapturedTypes...>::type bind_last(CapturedTypes... CapturedArgs)
153+
{
154+
using ReturnFP = typename detail::RemoveLastArgs<ReturnType(),ReturnType(ArgTypes...),CapturedTypes...>::type;
155+
static_assert(std::is_same<ReturnFP, functional::Function<void(int,int)> >::value, "oops");
156+
ReturnFP f(detail::bind_last(ReturnFP(), *this, detail::forward<CapturedTypes>(CapturedArgs)...));
117157
return f;
118158
}
119159

120-
~Function() {
121-
ref && ref->dec() && ref->getAllocator()->free(ref);
160+
~Function()
161+
{
162+
if (ref) {
163+
if (ref->dec() == 0) {
164+
ref->get_allocator()->free(ref);
165+
}
166+
}
122167
}
123-
Function & operator = (const Function & rhs) {
124-
ref && ref->dec() && ref->getAllocator()->free(ref);
168+
Function & operator = (const Function & rhs)
169+
{
170+
if (ref) {
171+
if (ref->dec() == 0) {
172+
ref->get_allocator()->free(ref);
173+
}
174+
}
125175
ref = rhs.ref;
126-
ref && ref->inc();
176+
if(ref) {
177+
ref->inc();
178+
}
127179
}
128180
inline ReturnType operator () (ArgTypes&&... Args) {
129181
return (*ref)(detail::forward<ArgTypes>(Args)...);

source/v2/functional.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,6 @@
5353
#define FUNCTORFP_GROWBY FUNCTORFP_INITIAL
5454
#endif
5555

56-
#if YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE
57-
#define FUNCTOR_SIZE YOTTA_CONFIG_CORE_UTIL_FUNCTIONAL_FUNCTOR_SIZE
58-
#else
59-
#define FUNCTOR_SIZE (sizeof(MemberContainer<UnknownClass,void()>) + sizeof(void*) * 3)
60-
#endif
61-
6256
namespace functional {
6357
namespace detail {
6458
const UAllocTraits_t NeverFreeTrait = {.flags = UALLOC_TRAITS_NEVER_FREE};

0 commit comments

Comments
 (0)