| |
| // (C) Copyright Tobias Schwinger |
| // |
| // Use modification and distribution are subject to the boost Software License, |
| // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). |
| |
| //------------------------------------------------------------------------------ |
| // |
| // This example implements interfaces. |
| // |
| // Detailed description |
| // ==================== |
| // |
| // An interface is a collection of member function prototypes that may be |
| // implemented by classes. Objects of classes that implement the interface can |
| // then be assigned to an interface variable through which the interface's |
| // functions can be called. |
| // |
| // Interfaces are a feature of the Java programming language [Gosling] and the |
| // most obvious way to model interfaces in C++ is (multiple) inheritance. |
| // Using inheritance for this purpose, however, is neither the most efficient |
| // nor the most flexible solution, because: |
| // |
| // - all functions must be virtual, |
| // |
| // => a function that calls another function of the interface must do so |
| // via virtual dispatch (as opposed to inlining) |
| // => a class can not implement an interface's (overloaded) function via |
| // a function template |
| // |
| // - inhertitance is intrusive |
| // |
| // => object size increases |
| // => client's are always polymorphic |
| // => dependencies cause tighter coupling |
| // |
| // Fortunately it is possible to eliminate all the drawbacks mentioned above |
| // based on an alternative implementation proposed by David Abrahams. |
| // We'll add some detail to the original scheme (see [Abrahams]) such as |
| // support for overloaded and const qualified functions. |
| // The implementation in this example uses Boost.FunctionTypes to shift |
| // metaprogramming code from the preprocessor into templates, to reduce |
| // preprocessing time and increase maintainability. |
| // |
| // |
| // Limitations |
| // =========== |
| // |
| // There is no lifetime management as implemented by the Boost candidate |
| // Interfaces library (see [Turkanis]). |
| // |
| // This example does not compile with Visual C++. Template argument deduction |
| // from the result of the address-of operator does not work properly with this |
| // compiler. It is possible to partially work around the problem, but it isn't |
| // done here for the sake of readability. |
| // |
| // |
| // Bibliography |
| // ============ |
| // |
| // [Gosling] Gosling, J., Joy, B., Steele, G. The Java Language Specification |
| // http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html |
| // |
| // [Abrahams] Abrahams, D. Proposal: interfaces, Post to newsgroup comp.std.c++ |
| // http://groups.google.com/group/comp.std.c++/msg/85af30a61bf677e4 |
| // |
| // [Turkanis] Turkanis, J., Diggins, C. Boost candidate Interfaces library |
| // http://www.kangaroologic.com/interfaces/libs/interfaces/doc/index.html |
| |
| #include <cstddef> |
| |
| #include <boost/function_types/function_pointer.hpp> |
| #include <boost/function_types/member_function_pointer.hpp> |
| |
| #include <boost/config.hpp> |
| #include <boost/detail/workaround.hpp> |
| |
| #include <boost/utility/addressof.hpp> |
| |
| #include <boost/mpl/at.hpp> |
| #include <boost/mpl/vector.hpp> |
| #include <boost/mpl/joint_view.hpp> |
| #include <boost/mpl/single_view.hpp> |
| #include <boost/mpl/transform_view.hpp> |
| |
| #include <boost/preprocessor/seq/seq.hpp> |
| #include <boost/preprocessor/seq/enum.hpp> |
| #include <boost/preprocessor/seq/elem.hpp> |
| #include <boost/preprocessor/seq/size.hpp> |
| #include <boost/preprocessor/tuple/elem.hpp> |
| #include <boost/preprocessor/arithmetic/dec.hpp> |
| #include <boost/preprocessor/arithmetic/inc.hpp> |
| #include <boost/preprocessor/facilities/empty.hpp> |
| #include <boost/preprocessor/facilities/identity.hpp> |
| #include <boost/preprocessor/punctuation/comma_if.hpp> |
| #include <boost/preprocessor/iteration/local.hpp> |
| #include <boost/preprocessor/repetition/enum.hpp> |
| #include <boost/preprocessor/repetition/enum_params.hpp> |
| #include <boost/preprocessor/repetition/enum_binary_params.hpp> |
| #include <boost/preprocessor/repetition/enum_trailing_params.hpp> |
| |
| #include "detail/param_type.hpp" |
| |
| namespace example |
| { |
| namespace ft = boost::function_types; |
| namespace mpl = boost::mpl; |
| using namespace mpl::placeholders; |
| |
| // join a single type and an MPL-sequence |
| // in some ways similar to mpl::push_front (but mpl::push_front requires |
| // an MPL Extensible Sequence and this template does not) |
| template<typename T, typename Seq> |
| struct concat_view |
| : mpl::joint_view<mpl::single_view<T>, Seq> |
| { }; |
| |
| // metafunction returning a function pointer type for a vtable entry |
| template<typename Inf> |
| struct vtable_entry |
| : ft::function_pointer |
| < concat_view< typename Inf::result, mpl::transform_view< |
| typename Inf::params, param_type<_> > > > |
| { }; |
| |
| // the expression '& member<MetaInfo,Tag>::wrap<& Class::Function> ' in an |
| // assignment context binds the member function Function of Class with the |
| // properties described by MetaInfo and Tag to the corresponding vtable |
| // entry |
| template<typename Inf, typename Tag> |
| struct member |
| { |
| typedef typename ft::member_function_pointer |
| < concat_view<typename Inf::result,typename Inf::params>,Tag |
| >::type |
| mem_func_ptr; |
| |
| typedef typename mpl::at_c<typename Inf::params,0>::type context; |
| |
| template<mem_func_ptr MemFuncPtr> |
| static typename Inf::result wrap(void* c) |
| { |
| return (reinterpret_cast<context*>(c)->*MemFuncPtr)(); |
| } |
| template<mem_func_ptr MemFuncPtr, typename T0> |
| static typename Inf::result wrap(void* c, T0 a0) |
| { |
| return (reinterpret_cast<context*>(c)->*MemFuncPtr)(a0); |
| } |
| template<mem_func_ptr MemFuncPtr, typename T0, typename T1> |
| static typename Inf::result wrap(void* c, T0 a0, T1 a1) |
| { |
| return (reinterpret_cast<context*>(c)->*MemFuncPtr)(a0,a1); |
| } |
| // continue with the preprocessor (the scheme should be clear, by now) |
| #define BOOST_PP_LOCAL_MACRO(n) \ |
| template<mem_func_ptr MemFuncPtr, BOOST_PP_ENUM_PARAMS(n,typename T)> \ |
| static typename Inf::result wrap(void* c, \ |
| BOOST_PP_ENUM_BINARY_PARAMS(n,T,a)) \ |
| { \ |
| return (reinterpret_cast<context*>(c)->*MemFuncPtr)( \ |
| BOOST_PP_ENUM_PARAMS(n,a) ); \ |
| } |
| #define BOOST_PP_LOCAL_LIMITS (3,BOOST_FT_MAX_ARITY-1) |
| #include BOOST_PP_LOCAL_ITERATE() |
| }; |
| |
| // extract a parameter by index |
| template<typename Inf, std::size_t Index> |
| struct param |
| : param_type< typename mpl::at_c< typename Inf::params,Index>::type > |
| { }; |
| } |
| |
| // the interface definition on the client's side |
| #define BOOST_EXAMPLE_INTERFACE(name,def) \ |
| class name \ |
| { \ |
| struct vtable \ |
| { \ |
| BOOST_EXAMPLE_INTERFACE__MEMBERS(def,VTABLE) \ |
| }; \ |
| \ |
| vtable const * ptr_vtable; \ |
| void * ptr_that; \ |
| \ |
| template<class T> struct vtable_holder \ |
| { \ |
| static vtable const val_vtable; \ |
| }; \ |
| \ |
| public: \ |
| \ |
| template<class T> \ |
| inline name (T & that) \ |
| : ptr_vtable(& vtable_holder<T>::val_vtable) \ |
| , ptr_that(boost::addressof(that)) \ |
| { } \ |
| \ |
| BOOST_EXAMPLE_INTERFACE__MEMBERS(def,FUNCTION) \ |
| }; \ |
| \ |
| template<typename T> \ |
| name ::vtable const name ::vtable_holder<T>::val_vtable \ |
| = { BOOST_EXAMPLE_INTERFACE__MEMBERS(def,INIT_VTABLE) } |
| |
| |
| #ifdef BOOST_PP_NIL // never defined -- a comment with syntax highlighting |
| |
| BOOST_EXAMPLE_INTERFACE( interface_x, |
| (( a_func, (void)(int), const_qualified )) |
| (( another_func, (int), non_const )) |
| ); |
| |
| // expands to: |
| class interface_x |
| { |
| struct vtable |
| { |
| // meta information for first function |
| template<typename T = void*> struct inf0 |
| { |
| typedef void result; |
| typedef ::boost::mpl::vector< T, int > params; |
| }; |
| // function pointer with void* context pointer and parameters optimized |
| // for forwarding |
| ::example::vtable_entry<inf0<> >::type func0; |
| |
| // second function |
| template<typename T = void*> struct inf1 |
| { |
| typedef int result; |
| typedef ::boost::mpl::vector< T > params; |
| }; |
| ::example::vtable_entry<inf1<> >::type func1; |
| }; |
| |
| // data members |
| vtable const * ptr_vtable; |
| void * ptr_that; |
| |
| // this template is instantiated for every class T this interface is created |
| // from, causing the compiler to emit an initialized vtable for this type |
| // (see aggregate assignment, below) |
| template<class T> struct vtable_holder |
| { |
| static vtable const val_vtable; |
| }; |
| |
| public: |
| |
| // converting ctor, creates an interface from an arbitrary class |
| template<class T> |
| inline interface_x (T & that) |
| : ptr_vtable(& vtable_holder<T>::val_vtable) |
| , ptr_that(boost::addressof(that)) |
| { } |
| |
| // the member functions from the interface definition, parameters are |
| // optimized for forwarding |
| |
| inline vtable::inf0<> ::result a_func ( |
| ::example::param<vtable::inf0<>,1>::type p0) const |
| { |
| return ptr_vtable-> func0(ptr_that , p0); |
| } |
| |
| inline vtable::inf1<> ::result another_func () |
| { |
| return ptr_vtable-> func1(ptr_that ); |
| } |
| }; |
| |
| template<typename T> |
| interface_x ::vtable const interface_x ::vtable_holder<T>::val_vtable = |
| { |
| // instantiate function templates that wrap member function pointers (which |
| // are known at compile time) by taking their addresses in assignment to |
| // function pointer context |
| & ::example::member< vtable::inf0<T>, ::example::ft:: const_qualified > |
| ::template wrap < &T:: a_func > |
| , & ::example::member< vtable::inf1<T>, ::example::ft:: non_const > |
| ::template wrap < &T:: another_func > |
| }; |
| #endif |
| |
| // preprocessing code details |
| |
| // iterate all of the interface's members and invoke a macro (prefixed with |
| // BOOST_EXAMPLE_INTERFACE_) |
| #define BOOST_EXAMPLE_INTERFACE__MEMBERS(seq,macro) \ |
| BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), \ |
| BOOST_EXAMPLE_INTERFACE__ ## macro,seq) |
| |
| // extract signature sequence from entry |
| #define BOOST_EXAMPLE_INTERFACE__VTABLE(z,i,seq) \ |
| BOOST_EXAMPLE_INTERFACE__VTABLE_I(z,i, \ |
| BOOST_PP_TUPLE_ELEM(3,1,BOOST_PP_SEQ_ELEM(i,seq))) |
| |
| // split the signature sequence result/params and insert T at the beginning of |
| // the params part |
| #define BOOST_EXAMPLE_INTERFACE__VTABLE_I(z,i,seq) \ |
| BOOST_EXAMPLE_INTERFACE__VTABLE_II(z,i, \ |
| BOOST_PP_SEQ_HEAD(seq),(T)BOOST_PP_SEQ_TAIL(seq)) |
| |
| // emit the meta information structure and function pointer declaration |
| #define BOOST_EXAMPLE_INTERFACE__VTABLE_II(z,i,result_type,param_types) \ |
| template<typename T = void*> \ |
| struct BOOST_PP_CAT(inf,i) \ |
| { \ |
| typedef result_type result; \ |
| typedef ::boost::mpl::vector< BOOST_PP_SEQ_ENUM(param_types) > params; \ |
| }; \ |
| ::example::vtable_entry<BOOST_PP_CAT(inf,i)<> >::type BOOST_PP_CAT(func,i); |
| |
| // extract tuple entry from sequence and precalculate the name of the function |
| // pointer variable |
| #define BOOST_EXAMPLE_INTERFACE__INIT_VTABLE(z,i,seq) \ |
| BOOST_EXAMPLE_INTERFACE__INIT_VTABLE_I(i,seq,BOOST_PP_CAT(func,i), \ |
| BOOST_PP_SEQ_ELEM(i,seq)) |
| |
| // emit a function pointer expression that encapsulates the corresponding |
| // member function of T |
| #define BOOST_EXAMPLE_INTERFACE__INIT_VTABLE_I(i,seq,func,desc) \ |
| BOOST_PP_COMMA_IF(i) & ::example::member< BOOST_PP_CAT(vtable::inf,i)<T>, \ |
| ::example::ft:: BOOST_PP_TUPLE_ELEM(3,2,desc) >::template wrap \ |
| < &T:: BOOST_PP_TUPLE_ELEM(3,0,desc) > |
| |
| // extract tuple entry from sequence |
| #define BOOST_EXAMPLE_INTERFACE__FUNCTION(z,i,seq) \ |
| BOOST_EXAMPLE_INTERFACE__FUNCTION_I(z,i,BOOST_PP_SEQ_ELEM(i,seq)) |
| |
| // precalculate function name, arity, name of meta info structure and cv- |
| // qualifiers |
| #define BOOST_EXAMPLE_INTERFACE__FUNCTION_I(z,i,desc) \ |
| BOOST_EXAMPLE_INTERFACE__FUNCTION_II(z,i, \ |
| BOOST_PP_TUPLE_ELEM(3,0,desc), \ |
| BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(3,1,desc))), \ |
| BOOST_PP_CAT(vtable::inf,i)<>, \ |
| BOOST_PP_CAT(BOOST_EXAMPLE_INTERFACE___,BOOST_PP_TUPLE_ELEM(3,2,desc)) \ |
| ) |
| |
| // emit the definition for a member function of the interface |
| #define BOOST_EXAMPLE_INTERFACE__FUNCTION_II(z,i,name,arity,types,cv) \ |
| inline types ::result name \ |
| (BOOST_PP_ENUM_ ## z (arity,BOOST_EXAMPLE_INTERFACE__PARAM,types)) cv() \ |
| { \ |
| return ptr_vtable-> BOOST_PP_CAT(func,i)(ptr_that \ |
| BOOST_PP_ENUM_TRAILING_PARAMS_Z(z,arity,p)); \ |
| } |
| |
| // emit a parameter of the function definition |
| #define BOOST_EXAMPLE_INTERFACE__PARAM(z,j,types) \ |
| ::example::param<types,BOOST_PP_INC(j)>::type BOOST_PP_CAT(p,j) |
| |
| // helper macros to map 'const_qualified' to 'const' an 'non_const' to '' |
| #define BOOST_EXAMPLE_INTERFACE___const_qualified BOOST_PP_IDENTITY(const) |
| #define BOOST_EXAMPLE_INTERFACE___non_const BOOST_PP_EMPTY |
| |
| |