/* 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" #include "callbackfwd.h" template inline void* convertToOpaque(Type* t) { return t; } template inline void* convertToOpaque(const Type* t) { return const_cast(t); } template inline void* convertToOpaque(Type& t) { return &t; } template inline void* convertToOpaque(const Type& t) { return const_cast(&t); } template class ConvertFromOpaque { }; template class ConvertFromOpaque { public: static Type& apply(void* p) { return *static_cast(p); } }; template class ConvertFromOpaque { public: static const Type& apply(void* p) { return *static_cast(p); } }; template class ConvertFromOpaque { public: static Type* apply(void* p) { return static_cast(p); } }; template class ConvertFromOpaque { public: static const Type* apply(void* p) { return static_cast(p); } }; template class BindFirstOpaque { typedef typename Caller::first_argument_type FirstBound; FirstBound firstBound; public: typedef typename Caller::result_type result_type; explicit BindFirstOpaque(FirstBound firstBound) : firstBound(firstBound) { } result_type operator()() const { return Caller::call(firstBound); } FirstBound getBound() const { return firstBound; } static result_type thunk(void* environment) { return Caller::call(ConvertFromOpaque::apply(environment)); } void* getEnvironment() const { return convertToOpaque(firstBound); } }; template class BindFirstOpaque1 { typedef typename Caller::first_argument_type FirstBound; FirstBound firstBound; public: typedef typename Caller::second_argument_type first_argument_type; typedef typename Caller::result_type result_type; explicit BindFirstOpaque1(FirstBound firstBound) : firstBound(firstBound) { } result_type operator()(first_argument_type a1) const { return Caller::call(firstBound, a1); } FirstBound getBound() const { return firstBound; } static result_type thunk(void* environment, first_argument_type a1) { return Caller::call(ConvertFromOpaque::apply(environment), a1); } void* getEnvironment() const { return convertToOpaque(firstBound); } }; template class BindFirstOpaque2 { typedef typename Caller::first_argument_type FirstBound; FirstBound firstBound; public: typedef typename Caller::second_argument_type first_argument_type; typedef typename Caller::third_argument_type second_argument_type; typedef typename Caller::result_type result_type; explicit BindFirstOpaque2(FirstBound firstBound) : firstBound(firstBound) { } result_type operator()(first_argument_type a1, second_argument_type a2) const { return Caller::call(firstBound, a1, a2); } FirstBound getBound() const { return firstBound; } static result_type thunk(void* environment, first_argument_type a1, second_argument_type a2) { return Caller::call(ConvertFromOpaque::apply(environment), a1, a2); } void* getEnvironment() const { return convertToOpaque(firstBound); } }; template class BindFirstOpaque3 { typedef typename Caller::first_argument_type FirstBound; FirstBound firstBound; public: typedef typename Caller::second_argument_type first_argument_type; typedef typename Caller::third_argument_type second_argument_type; typedef typename Caller::fourth_argument_type third_argument_type; typedef typename Caller::result_type result_type; explicit BindFirstOpaque3(FirstBound firstBound) : firstBound(firstBound) { } result_type operator()(first_argument_type a1, second_argument_type a2, third_argument_type a3) const { return Caller::call(firstBound, a1, a2, a3); } FirstBound getBound() const { return firstBound; } static result_type thunk(void* environment, first_argument_type a1, second_argument_type a2, third_argument_type a3) { return Caller::call(ConvertFromOpaque::apply(environment), a1, a2, a3); } void* getEnvironment() const { return convertToOpaque(firstBound); } }; 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()); } /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer. /// /// Use with the callback constructors MemberCaller, ConstMemberCaller, ReferenceCaller, ConstReferenceCaller, PointerCaller, ConstPointerCaller and FreeCaller. template class Callback0 : public CallbackBase { typedef CallbackBase Base; static Result nullThunk(void*) { } public: typedef Result result_type; Callback0() : Base(0, nullThunk) { } template Callback0(const BindFirstOpaque& caller) : Base(caller.getEnvironment(), BindFirstOpaque::thunk) { } Callback0(void* environment, Thunk function) : Base(environment, function) { } result_type operator()() const { return getThunk()(getEnvironment()); } }; template inline Callback0 makeCallback0(const Caller& caller, typename Caller::first_argument_type callee) { return Callback0(BindFirstOpaque(callee)); } template inline Callback0 makeStatelessCallback0(const Caller& caller) { return makeCallback0(Caller0To1(), 0); } typedef Callback0 Callback; /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer and one other argument. /// /// Use with the callback constructors MemberCaller1, ConstMemberCaller1, ReferenceCaller1, ConstReferenceCaller1, PointerCaller1, ConstPointerCaller1 and FreeCaller1. template class Callback1 : public CallbackBase { typedef CallbackBase Base; static Result nullThunk(void*, FirstArgument) { } public: typedef FirstArgument first_argument_type; typedef Result result_type; Callback1() : Base(0, nullThunk) { } template Callback1(const BindFirstOpaque1& caller) : Base(caller.getEnvironment(), BindFirstOpaque1::thunk) { } Callback1(void* environment, Thunk function) : Base(environment, function) { } result_type operator()(FirstArgument firstArgument) const { return getThunk()(getEnvironment(), firstArgument); } }; template inline Callback1 makeCallback1(const Caller& caller, typename Caller::first_argument_type callee) { return Callback1(BindFirstOpaque1(callee)); } template inline Callback1 makeStatelessCallback1(const Caller& caller) { return makeCallback1(Caller1To2(), 0); } /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer and two other arguments. /// template class Callback2 : public CallbackBase { typedef CallbackBase Base; static Result nullThunk(void*, FirstArgument, SecondArgument) { } public: typedef FirstArgument first_argument_type; typedef SecondArgument second_argument_type; typedef Result result_type; Callback2() : Base(0, nullThunk) { } template Callback2(const BindFirstOpaque2& caller) : Base(caller.getEnvironment(), BindFirstOpaque2::thunk) { } Callback2(void* environment, Thunk function) : Base(environment, function) { } result_type operator()(FirstArgument firstArgument, SecondArgument secondArgument) const { return getThunk()(getEnvironment(), firstArgument, secondArgument); } }; template inline Callback2< typename Caller::second_argument_type, typename Caller::third_argument_type, typename Caller::result_type > makeCallback2(const Caller& caller, typename Caller::first_argument_type callee) { return Callback2< typename Caller::second_argument_type, typename Caller::third_argument_type, typename Caller::result_type >(BindFirstOpaque2(callee)); } template inline Callback2< typename Caller::first_argument_type, typename Caller::second_argument_type, typename Caller::result_type > makeStatelessCallback2(const Caller& caller) { return makeCallback2(Caller2To3(), 0); } /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer and three other arguments. /// template class Callback3 : public CallbackBase { typedef CallbackBase Base; static Result nullThunk(void*, FirstArgument, SecondArgument, ThirdArgument) { } public: typedef FirstArgument first_argument_type; typedef SecondArgument second_argument_type; typedef ThirdArgument third_argument_type; typedef Result result_type; Callback3() : Base(0, nullThunk) { } template Callback3(const BindFirstOpaque3& caller) : Base(caller.getEnvironment(), BindFirstOpaque3::thunk) { } Callback3(void* environment, Thunk function) : Base(environment, function) { } result_type operator()(FirstArgument firstArgument, SecondArgument secondArgument, ThirdArgument thirdArgument) const { return getThunk()(getEnvironment(), firstArgument, secondArgument, thirdArgument); } }; template inline Callback3< typename Caller::second_argument_type, typename Caller::third_argument_type, typename Caller::fourth_argument_type, typename Caller::result_type > makeCallback3(const Caller& caller, typename Caller::first_argument_type callee) { return Callback3< typename Caller::second_argument_type, typename Caller::third_argument_type, typename Caller::fourth_argument_type, typename Caller::result_type >(BindFirstOpaque3(callee)); } template inline Callback3< typename Caller::first_argument_type, typename Caller::second_argument_type, typename Caller::third_argument_type, typename Caller::result_type > makeStatelessCallback3(const Caller& caller) { return makeCallback3(Caller3To4(), 0); } /// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function. /// /// \dontinclude generic/callback.cpp /// \skipline MemberCaller example /// \until end example template class MemberCaller : public BindFirstOpaque< Member > { public: MemberCaller(Environment& environment) : BindFirstOpaque< Member >(environment) { } }; /// \brief Forms a Callback from a const Environment reference and a const Environment member-function. /// /// \dontinclude generic/callback.cpp /// \skipline MemberCaller example /// \until end example template class ConstMemberCaller : public BindFirstOpaque< ConstMember > { public: ConstMemberCaller(const Environment& environment) : BindFirstOpaque< ConstMember >(environment) { } }; /// \brief Forms a Callback from a non-const Environment reference and a const Environment member-function which takes one argument. template class MemberCaller1 : public BindFirstOpaque1< Member1 > { public: MemberCaller1(Environment& environment) : BindFirstOpaque1< Member1 >(environment) { } }; /// \brief Forms a Callback from a const Environment reference and a const Environment member-function which takes one argument. template class ConstMemberCaller1 : public BindFirstOpaque1< ConstMember1 > { public: ConstMemberCaller1(const Environment& environment) : BindFirstOpaque1< ConstMember1 >(environment) { } }; /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference. /// /// \dontinclude generic/callback.cpp /// \skipline ReferenceCaller example /// \until end example template class ReferenceCaller : public BindFirstOpaque< Function1 > { public: ReferenceCaller(Environment& environment) : BindFirstOpaque< Function1 >(environment) { } }; /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference. /// /// \dontinclude generic/callback.cpp /// \skipline ReferenceCaller example /// \until end example template class ConstReferenceCaller : public BindFirstOpaque< Function1 > { public: ConstReferenceCaller(const Environment& environment) : BindFirstOpaque< Function1 >(environment) { } }; /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference and one other argument. template class ReferenceCaller1 : public BindFirstOpaque1< Function2 > { public: ReferenceCaller1(Environment& environment) : BindFirstOpaque1< Function2 >(environment) { } }; /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference and one other argument. template class ConstReferenceCaller1 : public BindFirstOpaque1< Function2 > { public: ConstReferenceCaller1(const Environment& environment) : BindFirstOpaque1< Function2 >(environment) { } }; /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer. template class PointerCaller : public BindFirstOpaque< Function1 > { public: PointerCaller(Environment* environment) : BindFirstOpaque< Function1 >(environment) { } }; /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer. template class ConstPointerCaller : public BindFirstOpaque< Function1 > { public: ConstPointerCaller(const Environment* environment) : BindFirstOpaque< Function1 >(environment) { } }; /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer and one other argument. template class PointerCaller1 : public BindFirstOpaque1< Function2 > { public: PointerCaller1(Environment* environment) : BindFirstOpaque1< Function2 >(environment) { } }; /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer and one other argument. template class ConstPointerCaller1 : public BindFirstOpaque1< Function2 > { public: ConstPointerCaller1(const Environment* environment) : BindFirstOpaque1< Function2 >(environment) { } }; /// \brief Forms a Callback from a free function which takes no arguments. template class FreeCaller : public BindFirstOpaque< Caller0To1< Function0 > > { public: FreeCaller() : BindFirstOpaque< Caller0To1< Function0 > >(0) { } }; /// \brief Forms a Callback from a free function which takes a single argument. template class FreeCaller1 : public BindFirstOpaque1< Caller1To2< Function1 > > { public: FreeCaller1() : BindFirstOpaque1< Caller1To2< Function1 > >(0) { } }; /// \brief Constructs a Callback from a non-const \p functor with zero arguments. /// /// \param Functor Must define \c operator()(). template inline Callback makeCallback(Functor& functor) { return Callback(MemberCaller(functor)); } /// \brief Constructs a Callback from a const \p functor with zero arguments. /// /// \param Functor Must define const \c operator()(). template inline Callback makeCallback(const Functor& functor) { return Callback(ConstMemberCaller(functor)); } /// \brief Constructs a Callback1 from a non-const \p functor with one argument. /// /// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type). template inline Callback1 makeCallback1(Functor& functor) { typedef typename Functor::first_argument_type FirstArgument; return Callback1(MemberCaller1(functor)); } /// \brief Constructs a Callback1 from a const \p functor with one argument. /// /// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type). template inline Callback1 makeCallback1(const Functor& functor) { typedef typename Functor::first_argument_type FirstArgument; return Callback1(ConstMemberCaller1(functor)); } typedef Callback1 BoolImportCallback; typedef Callback1 BoolExportCallback; typedef Callback1 IntImportCallback; typedef Callback1 IntExportCallback; typedef Callback1 FloatImportCallback; typedef Callback1 FloatExportCallback; typedef Callback1 StringImportCallback; typedef Callback1 StringExportCallback; typedef Callback1 SizeImportCallback; typedef Callback1 SizeExportCallback; #endif