-
Notifications
You must be signed in to change notification settings - Fork 17
Lambdas #44
base: master
Are you sure you want to change the base?
Lambdas #44
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* 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 __FUNCTIONPOINTER_HPP__ | ||
#define __FUNCTIONPOINTER_HPP__ | ||
|
||
#include <string.h> | ||
#include <stdint.h> | ||
#include <stddef.h> | ||
#include <stdarg.h> | ||
#include <new> | ||
#include <utility> | ||
#include <cstdio> | ||
#include <type_traits> | ||
|
||
namespace mbed { | ||
namespace util { | ||
|
||
|
||
namespace impl{ | ||
|
||
template<size_t I = 0> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is my understanding correct that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
class FunctionPointerStorage { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FunctionPointerStorage is used in FunctionPointerInterface. |
||
public: | ||
// 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)(); | ||
|
||
union { | ||
void * _static_fp; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is this different from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are syntactically equivalent, but semantically different. The static FP is a locally captured pointer, while the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name |
||
struct { | ||
union { | ||
char _member[sizeof(UnknownFunctionMember_t)]; | ||
UnknownFunctionMember_t _alignementAndSizeGuarantees; | ||
}; | ||
void * _object; | ||
} _method; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we have a better name than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could call it something else, but it really is a method pointer. It points to a specific method on a specific object. I'm open to suggestions for other names, but so far, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a function member (method doesn't exist in c++ wording) bound to an instance maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's at least call it |
||
char _raw_storage[sizeof(void*) + sizeof(UnknownFunctionMember_t) + I]; | ||
void * _external_functor; | ||
}; | ||
}; | ||
|
||
template<size_t I = 0> | ||
class FunctionPointerSize0 { | ||
public: | ||
FunctionPointerStorage<I> _fp; | ||
virtual void nullmethod()=0; | ||
}; | ||
|
||
template<size_t I = 0> | ||
class FunctionPointerSize : public FunctionPointerSize0<I> { | ||
public: | ||
void nullmethod() {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you make explicit why you need |
||
FunctionPointerSize(){} | ||
FunctionPointerSize(const FunctionPointerSize &){} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we simply provide a constexpr member called |
||
}; | ||
template<class T> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If move and forward are not available on ARM CC, it would be nice if these utilities foes into their own header / or better, in compiler pollyfill, if applicable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. They will start their lives here, but will be moved into compiler polyfill as and when that is possible. |
||
T&& forward(typename std::remove_reference<T>::type& a) noexcept | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it useful to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. std::forward is not tagged as noexcept, maybe it can be good to follow the standard. |
||
{ | ||
return static_cast<T&&>(a); | ||
} | ||
template<class T> | ||
T&& forward(typename std::remove_reference<T>::type&& a) noexcept | ||
{ | ||
return static_cast<T&&>(a); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't your There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's closer to what |
||
template <typename FunctionType, size_t I = 0> | ||
class FunctionPointerInterface; | ||
|
||
template<typename FunctionType, size_t I = 0> | ||
class StaticPointer; | ||
|
||
template<typename FunctionType, typename C, size_t I = 0> | ||
class MethodPointer; | ||
|
||
template<typename FunctionType, typename F, size_t I = 0> | ||
class FunctorPointer; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. perhaps add a comment here to indicate the end of the namespace |
||
|
||
template <typename OutputSignature, typename InputSignature, size_t I> | ||
class FPFunctor; | ||
|
||
template<typename FunctionType, size_t I = 0> | ||
class FunctionPointer; | ||
|
||
|
||
#include "impl/FP0.hpp" | ||
#include "impl/FP1.hpp" | ||
} // namespace util | ||
} // namespace mbed | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
/* 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 __FUNCTIONPOINTER_IMPL_FP0_HPP__ | ||
#define __FUNCTIONPOINTER_IMPL_FP0_HPP__ | ||
#ifndef __FUNCTIONPOINTER_HPP__ | ||
#error FP0.hpp is an internal file that should only be called from inside FunctionPointer.hpp | ||
#endif | ||
|
||
template<typename R, size_t I> | ||
class FunctionPointer<R(), I> { | ||
public: | ||
typedef struct arg_struct{ | ||
} ArgStruct; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the need for |
||
typedef R(*static_fp)(void); | ||
|
||
FunctionPointer(R (*fp)()) { | ||
new(_storage) impl::StaticPointer<R(), I>(fp); | ||
} | ||
template <typename C> | ||
FunctionPointer(C *c, R (C::*member)()) { | ||
new(_storage) impl::MethodPointer<R(), C, I>(c, member); | ||
} | ||
template <typename F> | ||
FunctionPointer(const F & f) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to add the move constructor ? It will not get generated automatically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack |
||
new(_storage) impl::FunctorPointer<R(), F, I>(f); | ||
} | ||
operator bool() const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be safer to make this conversion explicit, like std::function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack |
||
return *reinterpret_cast<impl::FunctionPointerInterface<R()> *>(&_storage); | ||
} | ||
bool operator ==(const FunctionPointer<R()> & rhs) const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would be nice to add operator != There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||
return *reinterpret_cast<impl::FunctionPointerInterface<R()> *>(&_storage) == | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comparison is not accurate because it allow to compare carrots and potatoes. But it is a difficult problem to solve. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless there is a way to compare to instantiations of a base class and validate that they have the same vtable pointer, I'm not sure this is solvable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using external vtable instead of inheritance, it is possible to make a valid operator== but the complexity will increase by an order of magnitude for peoples non initiated. (
With an external vtable, we can control more finely template instantiations and it became possible to instantiate:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't that require RTTI? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, you're right. I take that back. |
||
*reinterpret_cast<impl::FunctionPointerInterface<R()> *>(rhs.storage); | ||
} | ||
FunctionPointer<R()> & operator=(const FunctionPointer<R()> & rhs) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to add the move assignment operator ? It will not get generated automatically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack |
||
reinterpret_cast<impl::FunctionPointerInterface<R()>*>(_storage)->~FunctionPointerInterface<R()>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think I understand the semantics here. Why does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you assign to a function pointer, you must clear out the contents of the internal storage before installing something new into the storage. The storage is always populated using |
||
rhs.copy_to(_storage); | ||
return *this; | ||
} | ||
inline R operator ()() const { | ||
return call(); | ||
} | ||
R call() const { | ||
return reinterpret_cast<impl::FunctionPointerInterface<R()>*>(_storage)->call(); | ||
} | ||
~FunctionPointer() { | ||
reinterpret_cast<impl::FunctionPointerInterface<R()>*>(_storage)->~FunctionPointerInterface<R()>(); | ||
} | ||
protected: | ||
union { | ||
char _storage[sizeof(impl::FunctionPointerSize<I>)]; | ||
impl::FunctionPointerSize<I> _alignementAndSizeGuarantees; | ||
}; | ||
}; | ||
|
||
namespace impl { | ||
template <typename R, size_t I> | ||
class FunctionPointerInterface<R(),I>{ | ||
public: | ||
virtual operator bool() const = 0; | ||
virtual bool operator ==(const FunctionPointerInterface &) const = 0; | ||
virtual R call(void) const = 0; | ||
virtual ~FunctionPointerInterface(){}; | ||
virtual void copy_to(void *) const = 0; | ||
protected: | ||
FunctionPointerStorage<I> _fp; | ||
}; | ||
|
||
/** A class for storing and calling a pointer to a static or member void function without arguments | ||
*/ | ||
template<typename R, size_t I> | ||
class StaticPointer<R(), I> : public FunctionPointerInterface<R(), I>{ | ||
public: | ||
StaticPointer(R (*function)() = 0) | ||
{ | ||
attach(function); | ||
} | ||
StaticPointer(const StaticPointer& fp) { | ||
*this = fp; | ||
} | ||
void attach(R (*function)()) { | ||
this->_fp._static_fp = (void*)function; | ||
} | ||
R call() const{ | ||
return reinterpret_cast<R (*)()>(this->_fp._static_fp)(); | ||
} | ||
R (*get_function() const)() { | ||
return this->_fp._static_fp; | ||
} | ||
operator bool() const { | ||
return (this->_fp._static_fp != NULL); | ||
} | ||
bool operator==(const FunctionPointerInterface<R()> & rhs) const { | ||
return (this->_fp._static_fp == static_cast<const StaticPointer *>(&rhs)->_fp._static_fp); | ||
} | ||
StaticPointer & operator = (const StaticPointer & rhs) { | ||
this->_fp._static_fp = rhs._fp._static_fp; | ||
return *this; | ||
} | ||
void copy_to(void * storage) const { | ||
new(storage) StaticPointer(*this); | ||
} | ||
}; | ||
|
||
template<typename R, typename C, size_t I> | ||
class MethodPointer<R(), C, I> : public FunctionPointerInterface<R(), I>{ | ||
public: | ||
MethodPointer(C *object, R (C::*member)(void)) | ||
{ | ||
attach(object, member); | ||
} | ||
MethodPointer(const MethodPointer& fp) { | ||
*this = fp; | ||
} | ||
void attach(C *object, R (C::*member)(void)) { | ||
this->_fp._method._object = (void *) object; | ||
*reinterpret_cast<R (C::**)(void)>(this->_fp._method._member) = member; | ||
} | ||
R call() const{ | ||
C* o = static_cast<C*>(this->_fp._method._object); | ||
R (C::**m)(void) = reinterpret_cast<R (C::**)(void)>(const_cast<char *>(this->_fp._method._member)); | ||
return (o->**m)(); | ||
} | ||
R (*get_function() const)(){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we do without having this method for MethodPointer and others (except StaticPointer)? else if it is generic, it should be in FunctionPointerInterface |
||
return NULL; | ||
} | ||
operator bool() const { | ||
return (this->_fp._method._object != NULL); | ||
} | ||
bool operator ==(const FunctionPointerInterface<R()> & rhs) const { | ||
return (this->_fp._method._object == static_cast<const MethodPointer *>(&rhs)->_fp._method._object) && | ||
(0 == memcmp(this->_fp._method._member, static_cast<const MethodPointer *>(&rhs)->_fp._method._member, sizeof(this->_fp._method._member))); | ||
} | ||
MethodPointer & operator = (const MethodPointer & rhs) { | ||
this->_fp._method._object = rhs._fp._method._object; | ||
memcpy(this->_fp._method._member, rhs._fp._method._member, sizeof(this->_fp._method._member)); | ||
return *this; | ||
} | ||
void copy_to(void * storage) const { | ||
new(storage) MethodPointer(*this); | ||
} | ||
}; | ||
|
||
|
||
|
||
template<typename R, typename F, size_t I> | ||
class FunctorPointer<R(), F, I> : public FunctionPointerInterface<R(), I>{ | ||
constexpr static const bool Internal = sizeof(F) <= sizeof(impl::FunctionPointerStorage<I>); | ||
public: | ||
FunctorPointer(const F & f) | ||
{ | ||
attach(f); | ||
} | ||
FunctorPointer(const FunctorPointer & fp) | ||
{ | ||
*this = fp; | ||
} | ||
template <typename T = F> | ||
typename std::enable_if<Internal && std::is_same<T,F>::value>::type | ||
attach(const F & f) | ||
{ | ||
new(this->_fp._raw_storage)F(f); | ||
} | ||
template <typename T = F> | ||
typename std::enable_if<!Internal && std::is_same<T,F>::value>::type | ||
attach(const F & f) { | ||
this->_fp._external_functor = new F(f); | ||
} | ||
R call() const{ | ||
return const_ref()(); | ||
} | ||
R (*get_function() const)(){ | ||
return NULL; | ||
} | ||
operator bool() const { | ||
return true; | ||
} | ||
bool operator ==(const FunctionPointerInterface<R(), I> & rhs) const { | ||
return false; | ||
} | ||
FunctorPointer & operator = (const FunctorPointer & rhs) { | ||
deallocate(); | ||
attach(*reinterpret_cast<const F *>(rhs._fp._raw_storage)); | ||
return *this; | ||
} | ||
~FunctorPointer() { | ||
deallocate(); | ||
} | ||
void copy_to(void * storage) const { | ||
new(storage) FunctorPointer(*this); | ||
} | ||
protected: | ||
template <typename T = F> | ||
typename std::enable_if<Internal && std::is_same<T,F>::value, const F &>::type | ||
const_ref() const{ | ||
return *reinterpret_cast<const F *>(this->_fp._raw_storage); | ||
} | ||
template <typename T = F> | ||
typename std::enable_if<Internal && std::is_same<T,F>::value, F &>::type | ||
ref() { | ||
return *reinterpret_cast<F *>(this->_fp._raw_storage); | ||
} | ||
template <typename T = F> | ||
typename std::enable_if<Internal && std::is_same<T,F>::value>::type | ||
deallocate() { | ||
reinterpret_cast<F*>(this->_fp._raw_storage)->~F(); | ||
} | ||
|
||
template <typename T = F> | ||
typename std::enable_if<!Internal && std::is_same<T,F>::value, const F &>::type | ||
const_ref() const{ | ||
return *reinterpret_cast<const F *>(this->_fp._external_functor); | ||
} | ||
template <typename T = F> | ||
typename std::enable_if<!Internal && std::is_same<T,F>::value, F &>::type | ||
ref() { | ||
return *reinterpret_cast<F *>(this->_fp._external_functor); | ||
} | ||
template <typename T = F> | ||
typename std::enable_if<!Internal && std::is_same<T,F>::value>::type | ||
deallocate() { | ||
reinterpret_cast<F*>(this->_fp._external_functor)->~F(); | ||
} | ||
}; | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ifndef __CORE_UTIL_FUNCTIONPOINTER_HPP__
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Even more, though, it should be:
#ifndef __CORE_UTIL_V2_FUNCTIONPOINTER_HPP__