add functional::Function (generic wrapper around any callable type)
parent
87e324bb1c
commit
af9eff5c6d
|
@ -6,7 +6,10 @@
|
|||
#ifndef OCTA_FUNCTIONAL_H
|
||||
#define OCTA_FUNCTIONAL_H
|
||||
|
||||
#include "octa/new.h"
|
||||
#include "octa/memory.h"
|
||||
#include "octa/utility.h"
|
||||
#include "octa/traits.h"
|
||||
|
||||
namespace octa {
|
||||
#define OCTA_DEFINE_BINARY_OP(name, op, rettype) \
|
||||
|
@ -122,6 +125,287 @@ namespace octa {
|
|||
return ReferenceWrapper<T>(v);
|
||||
}
|
||||
template<typename T> void cref(const T &&) = delete;
|
||||
|
||||
/* function impl
|
||||
* reference: http://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction
|
||||
*/
|
||||
|
||||
template<typename> struct Function;
|
||||
|
||||
namespace internal {
|
||||
struct FunctorData {
|
||||
void *p1, *p2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FunctorInPlace {
|
||||
static constexpr bool value = sizeof(T) <= sizeof(FunctorData)
|
||||
&& (alignof(FunctorData) % alignof(T)) == 0;
|
||||
};
|
||||
|
||||
template<typename T, typename E = void>
|
||||
struct FunctorDataManager {
|
||||
template<typename R, typename ...A>
|
||||
static R call(const FunctorData &s, A ...args) {
|
||||
return ((T &)s)(forward<A>(args)...);
|
||||
}
|
||||
|
||||
static void store_f(FunctorData &s, T v) {
|
||||
new (&get_ref(s)) T(forward<T>(v));
|
||||
}
|
||||
|
||||
static void move_f(FunctorData &lhs, FunctorData &&rhs) {
|
||||
new (&get_ref(lhs)) T(move(get_ref(rhs)));
|
||||
}
|
||||
|
||||
static void destroy_f(FunctorData &s) {
|
||||
get_ref(s).~T();
|
||||
}
|
||||
|
||||
static T &get_ref(const FunctorData &s) {
|
||||
return (T &)s;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FunctorDataManager<T, typename enable_if<!FunctorInPlace<T>
|
||||
::value>::type> {
|
||||
template<typename R, typename ...A>
|
||||
static R call(const FunctorData &s, A ...args) {
|
||||
return (*(T *&)s)(forward<A>(args)...);
|
||||
}
|
||||
|
||||
static void store_f(FunctorData &s, T v) {
|
||||
new (&get_ptr_ref(s)) T *(new T(forward<T>(v)));
|
||||
}
|
||||
|
||||
static void move_f(FunctorData &lhs, FunctorData &&rhs) {
|
||||
new (&get_ptr_ref(lhs)) T *(get_ptr_ref(rhs));
|
||||
get_ptr_ref(rhs) = nullptr;
|
||||
}
|
||||
|
||||
static void destroy_f(FunctorData &s) {
|
||||
T *&ptr = get_ptr_ref(s);
|
||||
if (!ptr) return;
|
||||
delete ptr;
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
||||
static T &get_ref(const FunctorData &s) {
|
||||
return *get_ptr_ref(s);
|
||||
}
|
||||
|
||||
static T *&get_ptr_ref(FunctorData &s) {
|
||||
return (T *&)s;
|
||||
}
|
||||
|
||||
static T *&get_ptr_ref(const FunctorData &s) {
|
||||
return (T *&)s;
|
||||
}
|
||||
};
|
||||
|
||||
struct FunctionManager;
|
||||
|
||||
struct ManagerStorage {
|
||||
FunctorData data;
|
||||
const FunctionManager *manager;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static const FunctionManager &get_default_manager();
|
||||
|
||||
struct FunctionManager {
|
||||
template<typename T>
|
||||
inline static const FunctionManager create_default_manager() {
|
||||
return FunctionManager {
|
||||
&t_call_move_and_destroy<T>,
|
||||
&t_call_copy<T>,
|
||||
&t_call_destroy<T>
|
||||
};
|
||||
}
|
||||
|
||||
void (* const call_move_and_destroy)(ManagerStorage &lhs, ManagerStorage &&rhs);
|
||||
void (* const call_copy)(ManagerStorage &lhs, const ManagerStorage &rhs);
|
||||
void (* const call_destroy)(ManagerStorage &s);
|
||||
|
||||
template<typename T>
|
||||
static void t_call_move_and_destroy(ManagerStorage &lhs, ManagerStorage &&rhs) {
|
||||
typedef FunctorDataManager<T> spec;
|
||||
spec::move_f(lhs.data, move(rhs.data));
|
||||
spec::destroy_f(rhs.data);
|
||||
lhs.manager = &get_default_manager<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void t_call_copy(ManagerStorage &lhs, const ManagerStorage &rhs) {
|
||||
typedef FunctorDataManager<T> spec;
|
||||
lhs.manager = &get_default_manager<T>();
|
||||
spec::store_f(lhs.data, spec::get_ref(rhs.data));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void t_call_destroy(ManagerStorage &s) {
|
||||
typedef FunctorDataManager<T> spec;
|
||||
spec::destroy_f(s.data);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline static const FunctionManager &get_default_manager() {
|
||||
static const FunctionManager def_manager = FunctionManager::create_default_manager<T>();
|
||||
return def_manager;
|
||||
}
|
||||
|
||||
template<typename R, typename...>
|
||||
struct FunctionBase {
|
||||
struct type {
|
||||
typedef R result;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename R, typename T>
|
||||
struct FunctionBase<R, T> {
|
||||
struct type {
|
||||
typedef R result;
|
||||
typedef T argument;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename R, typename T, typename U>
|
||||
struct FunctionBase<R, T, U> {
|
||||
struct type {
|
||||
typedef R result;
|
||||
typedef T first;
|
||||
typedef U second;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
template<typename R, typename ...A>
|
||||
struct Function<R(A...)>: internal::FunctionBase<R, A...> {
|
||||
Function( ) { initialize_empty(); }
|
||||
Function(nullptr_t) { initialize_empty(); }
|
||||
|
||||
Function(Function &&f) {
|
||||
initialize_empty();
|
||||
swap(f);
|
||||
}
|
||||
|
||||
Function(const Function &f): p_call(f.p_call) {
|
||||
f.p_stor.manager->call_copy(p_stor, f.p_stor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Function(T f) {
|
||||
if (func_is_null(f)) {
|
||||
initialize_empty();
|
||||
return;
|
||||
}
|
||||
initialize(func_to_functor(forward<T>(f)));
|
||||
}
|
||||
|
||||
~Function() {
|
||||
p_stor.manager->call_destroy(p_stor);
|
||||
}
|
||||
|
||||
Function &operator=(Function &&f) {
|
||||
p_stor.manager->call_destroy(p_stor);
|
||||
swap(f);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Function &operator=(const Function &f) {
|
||||
p_stor.manager->call_destroy(p_stor);
|
||||
f.p_stor.manager->call_copy(p_stor, f.p_stor);
|
||||
return *this;
|
||||
};
|
||||
|
||||
R operator()(A ...args) const {
|
||||
return p_call(p_stor.data, forward<A>(args)...);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void assign(F &&f) {
|
||||
Function(forward<F>(f)).swap(*this);
|
||||
}
|
||||
|
||||
void swap(Function &f) {
|
||||
internal::ManagerStorage tmp;
|
||||
f.p_stor.manager->call_move_and_destroy(tmp, move(f.p_stor));
|
||||
p_stor.manager->call_move_and_destroy(f.p_stor, move(p_stor));
|
||||
tmp.manager->call_move_and_destroy(p_stor, move(tmp));
|
||||
octa::swap(p_call, f.p_call);
|
||||
}
|
||||
|
||||
operator bool() const { return p_call != nullptr; }
|
||||
|
||||
private:
|
||||
internal::ManagerStorage p_stor;
|
||||
R (*p_call)(const internal::FunctorData &, A...);
|
||||
|
||||
template<typename T>
|
||||
void initialize(T f) {
|
||||
p_call = &internal::FunctorDataManager<T>::template call<R, A...>;
|
||||
p_stor.manager = &internal::get_default_manager<T>();
|
||||
internal::FunctorDataManager<T>::store_f(p_stor.data, forward<T>(f));
|
||||
}
|
||||
|
||||
void initialize_empty() {
|
||||
typedef R(*emptyf)(A...);
|
||||
p_call = nullptr;
|
||||
p_stor.manager = &internal::get_default_manager<emptyf>();
|
||||
internal::FunctorDataManager<emptyf>::store_f(p_stor.data, nullptr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool func_is_null(const T &) { return false; }
|
||||
|
||||
static bool func_is_null(R (* const &fptr)(A...)) {
|
||||
return fptr == nullptr;
|
||||
}
|
||||
|
||||
template<typename RR, typename T, typename ...AA>
|
||||
static bool func_is_null(RR (T::* const &fptr)(AA...)) {
|
||||
return fptr == nullptr;
|
||||
}
|
||||
|
||||
template<typename RR, typename T, typename ...AA>
|
||||
static bool func_is_null(RR (T::* const &fptr)(AA...) const) {
|
||||
return fptr == nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T func_to_functor(T &&f) {
|
||||
return forward<T>(f);
|
||||
}
|
||||
|
||||
template<typename RR, typename T, typename ...AA>
|
||||
auto func_to_functor(RR (T::*f)(AA...)) -> decltype(mem_fn(f)) {
|
||||
return mem_fn(f);
|
||||
}
|
||||
|
||||
template<typename RR, typename T, typename ...AA>
|
||||
auto func_to_functor(RR (T::*f)(AA...) const) -> decltype(mem_fn(f)) {
|
||||
return mem_fn(f);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void swap(Function<T> &a, Function<T> &b) {
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator==(nullptr_t, const Function<T> &rhs) { return !rhs; }
|
||||
|
||||
template<typename T>
|
||||
bool operator==(const Function<T> &lhs, nullptr_t) { return !lhs; }
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(nullptr_t, const Function<T> &rhs) { return rhs; }
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(const Function<T> &lhs, nullptr_t) { return lhs; }
|
||||
}
|
||||
|
||||
#endif
|
|
@ -62,7 +62,6 @@ namespace octa {
|
|||
template<typename T, typename R, typename A>
|
||||
struct MemTypes<T, R(A)> {
|
||||
typedef R result;
|
||||
typedef T argument;
|
||||
typedef T first;
|
||||
typedef A second;
|
||||
};
|
||||
|
@ -74,7 +73,6 @@ namespace octa {
|
|||
template<typename T, typename R, typename A>
|
||||
struct MemTypes<T, R(A) const> {
|
||||
typedef R result;
|
||||
typedef const T argument;
|
||||
typedef const T first;
|
||||
typedef A second;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue