// (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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 struct concat_view : mpl::joint_view, Seq> { }; // metafunction returning a function pointer type for a vtable entry template struct vtable_entry : ft::function_pointer < concat_view< typename Inf::result, mpl::transform_view< typename Inf::params, param_type<_> > > > { }; // the expression '& member::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 struct member { typedef typename ft::member_function_pointer < concat_view,Tag >::type mem_func_ptr; typedef typename mpl::at_c::type context; template static typename Inf::result wrap(void* c) { return (reinterpret_cast(c)->*MemFuncPtr)(); } template static typename Inf::result wrap(void* c, T0 a0) { return (reinterpret_cast(c)->*MemFuncPtr)(a0); } template static typename Inf::result wrap(void* c, T0 a0, T1 a1) { return (reinterpret_cast(c)->*MemFuncPtr)(a0,a1); } // continue with the preprocessor (the scheme should be clear, by now) #define BOOST_PP_LOCAL_MACRO(n) \ template \ static typename Inf::result wrap(void* c, \ BOOST_PP_ENUM_BINARY_PARAMS(n,T,a)) \ { \ return (reinterpret_cast(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 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 struct vtable_holder \ { \ static vtable const val_vtable; \ }; \ \ public: \ \ template \ inline name (T & that) \ : ptr_vtable(& vtable_holder::val_vtable) \ , ptr_that(boost::addressof(that)) \ { } \ \ BOOST_EXAMPLE_INTERFACE__MEMBERS(def,FUNCTION) \ }; \ \ template \ name ::vtable const name ::vtable_holder::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 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 >::type func0; // second function template struct inf1 { typedef int result; typedef ::boost::mpl::vector< T > params; }; ::example::vtable_entry >::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 struct vtable_holder { static vtable const val_vtable; }; public: // converting ctor, creates an interface from an arbitrary class template inline interface_x (T & that) : ptr_vtable(& vtable_holder::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,1>::type p0) const { return ptr_vtable-> func0(ptr_that , p0); } inline vtable::inf1<> ::result another_func () { return ptr_vtable-> func1(ptr_that ); } }; template interface_x ::vtable const interface_x ::vtable_holder::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, ::example::ft:: const_qualified > ::template wrap < &T:: a_func > , & ::example::member< vtable::inf1, ::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 \ struct BOOST_PP_CAT(inf,i) \ { \ typedef result_type result; \ typedef ::boost::mpl::vector< BOOST_PP_SEQ_ENUM(param_types) > params; \ }; \ ::example::vtable_entry >::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), \ ::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::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