/* 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 */ #pragma once /// \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::value>::type> inline const void *convertToOpaque( const T *t ){ return t; } template struct ConvertFromOpaque { static const T *apply( void *p ){ return static_cast( p ); } }; template::value>::type> 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... ){ return R{}; } 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 operator()(arguments) and its signature as \c func. 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 const \c operator()(arguments) and its signature as \c func. 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 ); } // todo: remove using BoolImportCallback = Callback; using BoolExportCallback = Callback; using IntImportCallback = Callback; using IntExportCallback = Callback; using FloatImportCallback = Callback; using FloatExportCallback = Callback; using StringImportCallback = Callback; using StringExportCallback = Callback; using SizeImportCallback = Callback; using SizeExportCallback = Callback;