/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined( INCLUDED_GENERIC_CLOSURE_H ) #define INCLUDED_GENERIC_CLOSURE_H /// \file /// \brief Type-safe techniques for binding the first argument of an opaque callback. #include #include "functional.h" namespace detail { template class CallbackBase { void *m_environment; Thunk_ m_thunk; public: typedef Thunk_ Thunk; CallbackBase(void *environment, Thunk function) : m_environment(environment), m_thunk(function) { } void *getEnvironment() const { return m_environment; } Thunk getThunk() const { return m_thunk; } }; template inline bool operator==(const CallbackBase &self, const CallbackBase &other) { return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk(); } template inline bool operator!=(const CallbackBase &self, const CallbackBase &other) { return !(self == other); } template inline bool operator<(const CallbackBase &self, const CallbackBase &other) { return self.getEnvironment() < other.getEnvironment() || (!(other.getEnvironment() < self.getEnvironment()) && self.getThunk() < other.getThunk()); } } namespace detail { template struct ConvertFromOpaque { }; // reference template inline const void *convertToOpaque(const T &t) { return &t; } template struct ConvertFromOpaque { static T const &apply(void *p) { return *static_cast(p); } }; template inline void *convertToOpaque(T &t) { return &t; } template struct ConvertFromOpaque { static T &apply(void *p) { return *static_cast( p ); } }; // pointer template inline const void *convertToOpaque(const T *t) { return t; } template struct ConvertFromOpaque { static const T *apply(void *p) { return static_cast(p); } }; template inline void *convertToOpaque(T *t) { return t; } template struct ConvertFromOpaque { static T *apply(void *p) { return static_cast(p); } }; // function pointer template inline const void *convertToOpaque(R(*const &t)(Ts...)) { return &t; } template struct ConvertFromOpaque { using Type = R(*)(Ts...); static Type const &apply(void *p) { return *static_cast(p); } }; template inline void *convertToOpaque(R(*&t)(Ts...)) { return &t; } template struct ConvertFromOpaque { using Type = R(*)(Ts...); static Type &apply(void *p) { return *static_cast(p); } }; template class BindFirstOpaqueN; template class BindFirstOpaqueN { FirstBound firstBound; public: explicit BindFirstOpaqueN(FirstBound firstBound) : firstBound(firstBound) { } R operator()(Ts... args) const { return Caller::call(firstBound, args...); } FirstBound getBound() const { return firstBound; } static R thunk(void *environment, Ts... args) { return thunk_(detail::ConvertFromOpaque::apply(environment), args...); } static R thunk_(FirstBound environment, Ts... args) { return Caller::call(environment, args...); } void *getEnvironment() const { return const_cast(detail::convertToOpaque(firstBound)); } }; } template using BindFirstOpaque = detail::BindFirstOpaqueN>; /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer. /// /// Use with the callback constructors MemberCaller0, ConstMemberCaller0, ReferenceCaller0, ConstReferenceCaller0, PointerCaller0, ConstPointerCaller0 and FreeCaller0. template class Callback; template class Callback : public detail::CallbackBase { using Base = detail::CallbackBase; static R nullThunk(void *, Ts...) { } public: using func = R(Ts...); Callback() : Base(0, nullThunk) { } template Callback(const BindFirstOpaque &caller) : Base(caller.getEnvironment(), BindFirstOpaque::thunk) { } Callback(void *environment, typename Base::Thunk function) : Base(environment, function) { } R operator()(Ts... args) const { return Base::getThunk()(Base::getEnvironment(), args...); } }; namespace detail { template struct Arglist; template struct Arglist { using type = R(Head, Ts...); template using unshift = Arglist; using shift = Arglist; }; template struct Arglist { using type = R(Ts...); template using unshift = Arglist; }; template using ArgShift = typename detail::Arglist::shift::type; template using ArgUnshift = typename detail::Arglist::template unshift::type; } template inline Callback>> makeCallback(const Caller &caller, get_argument callee) { return BindFirstOpaque(callee); } template class CallerShiftFirst; template class CallerShiftFirst { public: using func = R(FirstArgument, Ts...); static R call(FirstArgument, Ts... args) { return Caller::call(args...); } }; template inline Callback> makeStatelessCallback(const Caller &caller) { return makeCallback(CallerShiftFirst, void *>>(), nullptr); } /// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function. template member> using MemberCaller = BindFirstOpaque>; /// \brief Constructs a Callback1 from a non-const \p functor /// /// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type). template inline Callback> makeCallback(Functor &functor) { return MemberCaller, &Functor::operator()>(functor); } /// \brief Forms a Callback from a const Environment reference and a const Environment member-function. template member> using ConstMemberCaller = BindFirstOpaque>; /// \brief Constructs a Callback1 from a const \p functor /// /// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type). template inline Callback> makeCallback(const Functor &functor) { return ConstMemberCaller, &Functor::operator()>(functor); } /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference. template *func> using ReferenceCaller = BindFirstOpaque, func>>; /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference. template *func> using ConstReferenceCaller = BindFirstOpaque, func>>; /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer. template *func> using PointerCaller = BindFirstOpaque, func>>; /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer. template *func> using ConstPointerCaller = BindFirstOpaque, func>>; namespace detail { template class FreeCaller : public BindFirstOpaque>> { public: FreeCaller() : BindFirstOpaque>>(nullptr) { } }; template struct FreeCallerWrapper; template struct FreeCallerWrapper { using func = R(void *, Ts...); static R call(void *f, Ts... args) { // ideally, we'd get the implementation of the function type directly. Instead, it's passed in return reinterpret_cast(f)(args...); } }; } /// \brief Forms a Callback from a free function template using FreeCaller = detail::FreeCaller, F>; template inline Callback makeCallbackF(R(*func)(Ts...)) { void *pVoid = reinterpret_cast(func); return BindFirstOpaque>(pVoid); } #endif