Brian Silverman | 57be316 | 2018-08-04 23:36:33 -0700 | [diff] [blame^] | 1 | |
| 2 | // (C) Copyright Tobias Schwinger |
| 3 | // |
| 4 | // Use modification and distribution are subject to the boost Software License, |
| 5 | // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). |
| 6 | |
| 7 | //------------------------------------------------------------------------------ |
| 8 | // |
| 9 | // This example implements interfaces. |
| 10 | // |
| 11 | // Detailed description |
| 12 | // ==================== |
| 13 | // |
| 14 | // An interface is a collection of member function prototypes that may be |
| 15 | // implemented by classes. Objects of classes that implement the interface can |
| 16 | // then be assigned to an interface variable through which the interface's |
| 17 | // functions can be called. |
| 18 | // |
| 19 | // Interfaces are a feature of the Java programming language [Gosling] and the |
| 20 | // most obvious way to model interfaces in C++ is (multiple) inheritance. |
| 21 | // Using inheritance for this purpose, however, is neither the most efficient |
| 22 | // nor the most flexible solution, because: |
| 23 | // |
| 24 | // - all functions must be virtual, |
| 25 | // |
| 26 | // => a function that calls another function of the interface must do so |
| 27 | // via virtual dispatch (as opposed to inlining) |
| 28 | // => a class can not implement an interface's (overloaded) function via |
| 29 | // a function template |
| 30 | // |
| 31 | // - inhertitance is intrusive |
| 32 | // |
| 33 | // => object size increases |
| 34 | // => client's are always polymorphic |
| 35 | // => dependencies cause tighter coupling |
| 36 | // |
| 37 | // Fortunately it is possible to eliminate all the drawbacks mentioned above |
| 38 | // based on an alternative implementation proposed by David Abrahams. |
| 39 | // We'll add some detail to the original scheme (see [Abrahams]) such as |
| 40 | // support for overloaded and const qualified functions. |
| 41 | // The implementation in this example uses Boost.FunctionTypes to shift |
| 42 | // metaprogramming code from the preprocessor into templates, to reduce |
| 43 | // preprocessing time and increase maintainability. |
| 44 | // |
| 45 | // |
| 46 | // Limitations |
| 47 | // =========== |
| 48 | // |
| 49 | // There is no lifetime management as implemented by the Boost candidate |
| 50 | // Interfaces library (see [Turkanis]). |
| 51 | // |
| 52 | // This example does not compile with Visual C++. Template argument deduction |
| 53 | // from the result of the address-of operator does not work properly with this |
| 54 | // compiler. It is possible to partially work around the problem, but it isn't |
| 55 | // done here for the sake of readability. |
| 56 | // |
| 57 | // |
| 58 | // Bibliography |
| 59 | // ============ |
| 60 | // |
| 61 | // [Gosling] Gosling, J., Joy, B., Steele, G. The Java Language Specification |
| 62 | // http://java.sun.com/docs/books/jls/third_edition/html/interfaces.html |
| 63 | // |
| 64 | // [Abrahams] Abrahams, D. Proposal: interfaces, Post to newsgroup comp.std.c++ |
| 65 | // http://groups.google.com/group/comp.std.c++/msg/85af30a61bf677e4 |
| 66 | // |
| 67 | // [Turkanis] Turkanis, J., Diggins, C. Boost candidate Interfaces library |
| 68 | // http://www.kangaroologic.com/interfaces/libs/interfaces/doc/index.html |
| 69 | |
| 70 | #include <cstddef> |
| 71 | |
| 72 | #include <boost/function_types/function_pointer.hpp> |
| 73 | #include <boost/function_types/member_function_pointer.hpp> |
| 74 | |
| 75 | #include <boost/config.hpp> |
| 76 | #include <boost/detail/workaround.hpp> |
| 77 | |
| 78 | #include <boost/utility/addressof.hpp> |
| 79 | |
| 80 | #include <boost/mpl/at.hpp> |
| 81 | #include <boost/mpl/vector.hpp> |
| 82 | #include <boost/mpl/joint_view.hpp> |
| 83 | #include <boost/mpl/single_view.hpp> |
| 84 | #include <boost/mpl/transform_view.hpp> |
| 85 | |
| 86 | #include <boost/preprocessor/seq/seq.hpp> |
| 87 | #include <boost/preprocessor/seq/enum.hpp> |
| 88 | #include <boost/preprocessor/seq/elem.hpp> |
| 89 | #include <boost/preprocessor/seq/size.hpp> |
| 90 | #include <boost/preprocessor/tuple/elem.hpp> |
| 91 | #include <boost/preprocessor/arithmetic/dec.hpp> |
| 92 | #include <boost/preprocessor/arithmetic/inc.hpp> |
| 93 | #include <boost/preprocessor/facilities/empty.hpp> |
| 94 | #include <boost/preprocessor/facilities/identity.hpp> |
| 95 | #include <boost/preprocessor/punctuation/comma_if.hpp> |
| 96 | #include <boost/preprocessor/iteration/local.hpp> |
| 97 | #include <boost/preprocessor/repetition/enum.hpp> |
| 98 | #include <boost/preprocessor/repetition/enum_params.hpp> |
| 99 | #include <boost/preprocessor/repetition/enum_binary_params.hpp> |
| 100 | #include <boost/preprocessor/repetition/enum_trailing_params.hpp> |
| 101 | |
| 102 | #include "detail/param_type.hpp" |
| 103 | |
| 104 | namespace example |
| 105 | { |
| 106 | namespace ft = boost::function_types; |
| 107 | namespace mpl = boost::mpl; |
| 108 | using namespace mpl::placeholders; |
| 109 | |
| 110 | // join a single type and an MPL-sequence |
| 111 | // in some ways similar to mpl::push_front (but mpl::push_front requires |
| 112 | // an MPL Extensible Sequence and this template does not) |
| 113 | template<typename T, typename Seq> |
| 114 | struct concat_view |
| 115 | : mpl::joint_view<mpl::single_view<T>, Seq> |
| 116 | { }; |
| 117 | |
| 118 | // metafunction returning a function pointer type for a vtable entry |
| 119 | template<typename Inf> |
| 120 | struct vtable_entry |
| 121 | : ft::function_pointer |
| 122 | < concat_view< typename Inf::result, mpl::transform_view< |
| 123 | typename Inf::params, param_type<_> > > > |
| 124 | { }; |
| 125 | |
| 126 | // the expression '& member<MetaInfo,Tag>::wrap<& Class::Function> ' in an |
| 127 | // assignment context binds the member function Function of Class with the |
| 128 | // properties described by MetaInfo and Tag to the corresponding vtable |
| 129 | // entry |
| 130 | template<typename Inf, typename Tag> |
| 131 | struct member |
| 132 | { |
| 133 | typedef typename ft::member_function_pointer |
| 134 | < concat_view<typename Inf::result,typename Inf::params>,Tag |
| 135 | >::type |
| 136 | mem_func_ptr; |
| 137 | |
| 138 | typedef typename mpl::at_c<typename Inf::params,0>::type context; |
| 139 | |
| 140 | template<mem_func_ptr MemFuncPtr> |
| 141 | static typename Inf::result wrap(void* c) |
| 142 | { |
| 143 | return (reinterpret_cast<context*>(c)->*MemFuncPtr)(); |
| 144 | } |
| 145 | template<mem_func_ptr MemFuncPtr, typename T0> |
| 146 | static typename Inf::result wrap(void* c, T0 a0) |
| 147 | { |
| 148 | return (reinterpret_cast<context*>(c)->*MemFuncPtr)(a0); |
| 149 | } |
| 150 | template<mem_func_ptr MemFuncPtr, typename T0, typename T1> |
| 151 | static typename Inf::result wrap(void* c, T0 a0, T1 a1) |
| 152 | { |
| 153 | return (reinterpret_cast<context*>(c)->*MemFuncPtr)(a0,a1); |
| 154 | } |
| 155 | // continue with the preprocessor (the scheme should be clear, by now) |
| 156 | #define BOOST_PP_LOCAL_MACRO(n) \ |
| 157 | template<mem_func_ptr MemFuncPtr, BOOST_PP_ENUM_PARAMS(n,typename T)> \ |
| 158 | static typename Inf::result wrap(void* c, \ |
| 159 | BOOST_PP_ENUM_BINARY_PARAMS(n,T,a)) \ |
| 160 | { \ |
| 161 | return (reinterpret_cast<context*>(c)->*MemFuncPtr)( \ |
| 162 | BOOST_PP_ENUM_PARAMS(n,a) ); \ |
| 163 | } |
| 164 | #define BOOST_PP_LOCAL_LIMITS (3,BOOST_FT_MAX_ARITY-1) |
| 165 | #include BOOST_PP_LOCAL_ITERATE() |
| 166 | }; |
| 167 | |
| 168 | // extract a parameter by index |
| 169 | template<typename Inf, std::size_t Index> |
| 170 | struct param |
| 171 | : param_type< typename mpl::at_c< typename Inf::params,Index>::type > |
| 172 | { }; |
| 173 | } |
| 174 | |
| 175 | // the interface definition on the client's side |
| 176 | #define BOOST_EXAMPLE_INTERFACE(name,def) \ |
| 177 | class name \ |
| 178 | { \ |
| 179 | struct vtable \ |
| 180 | { \ |
| 181 | BOOST_EXAMPLE_INTERFACE__MEMBERS(def,VTABLE) \ |
| 182 | }; \ |
| 183 | \ |
| 184 | vtable const * ptr_vtable; \ |
| 185 | void * ptr_that; \ |
| 186 | \ |
| 187 | template<class T> struct vtable_holder \ |
| 188 | { \ |
| 189 | static vtable const val_vtable; \ |
| 190 | }; \ |
| 191 | \ |
| 192 | public: \ |
| 193 | \ |
| 194 | template<class T> \ |
| 195 | inline name (T & that) \ |
| 196 | : ptr_vtable(& vtable_holder<T>::val_vtable) \ |
| 197 | , ptr_that(boost::addressof(that)) \ |
| 198 | { } \ |
| 199 | \ |
| 200 | BOOST_EXAMPLE_INTERFACE__MEMBERS(def,FUNCTION) \ |
| 201 | }; \ |
| 202 | \ |
| 203 | template<typename T> \ |
| 204 | name ::vtable const name ::vtable_holder<T>::val_vtable \ |
| 205 | = { BOOST_EXAMPLE_INTERFACE__MEMBERS(def,INIT_VTABLE) } |
| 206 | |
| 207 | |
| 208 | #ifdef BOOST_PP_NIL // never defined -- a comment with syntax highlighting |
| 209 | |
| 210 | BOOST_EXAMPLE_INTERFACE( interface_x, |
| 211 | (( a_func, (void)(int), const_qualified )) |
| 212 | (( another_func, (int), non_const )) |
| 213 | ); |
| 214 | |
| 215 | // expands to: |
| 216 | class interface_x |
| 217 | { |
| 218 | struct vtable |
| 219 | { |
| 220 | // meta information for first function |
| 221 | template<typename T = void*> struct inf0 |
| 222 | { |
| 223 | typedef void result; |
| 224 | typedef ::boost::mpl::vector< T, int > params; |
| 225 | }; |
| 226 | // function pointer with void* context pointer and parameters optimized |
| 227 | // for forwarding |
| 228 | ::example::vtable_entry<inf0<> >::type func0; |
| 229 | |
| 230 | // second function |
| 231 | template<typename T = void*> struct inf1 |
| 232 | { |
| 233 | typedef int result; |
| 234 | typedef ::boost::mpl::vector< T > params; |
| 235 | }; |
| 236 | ::example::vtable_entry<inf1<> >::type func1; |
| 237 | }; |
| 238 | |
| 239 | // data members |
| 240 | vtable const * ptr_vtable; |
| 241 | void * ptr_that; |
| 242 | |
| 243 | // this template is instantiated for every class T this interface is created |
| 244 | // from, causing the compiler to emit an initialized vtable for this type |
| 245 | // (see aggregate assignment, below) |
| 246 | template<class T> struct vtable_holder |
| 247 | { |
| 248 | static vtable const val_vtable; |
| 249 | }; |
| 250 | |
| 251 | public: |
| 252 | |
| 253 | // converting ctor, creates an interface from an arbitrary class |
| 254 | template<class T> |
| 255 | inline interface_x (T & that) |
| 256 | : ptr_vtable(& vtable_holder<T>::val_vtable) |
| 257 | , ptr_that(boost::addressof(that)) |
| 258 | { } |
| 259 | |
| 260 | // the member functions from the interface definition, parameters are |
| 261 | // optimized for forwarding |
| 262 | |
| 263 | inline vtable::inf0<> ::result a_func ( |
| 264 | ::example::param<vtable::inf0<>,1>::type p0) const |
| 265 | { |
| 266 | return ptr_vtable-> func0(ptr_that , p0); |
| 267 | } |
| 268 | |
| 269 | inline vtable::inf1<> ::result another_func () |
| 270 | { |
| 271 | return ptr_vtable-> func1(ptr_that ); |
| 272 | } |
| 273 | }; |
| 274 | |
| 275 | template<typename T> |
| 276 | interface_x ::vtable const interface_x ::vtable_holder<T>::val_vtable = |
| 277 | { |
| 278 | // instantiate function templates that wrap member function pointers (which |
| 279 | // are known at compile time) by taking their addresses in assignment to |
| 280 | // function pointer context |
| 281 | & ::example::member< vtable::inf0<T>, ::example::ft:: const_qualified > |
| 282 | ::template wrap < &T:: a_func > |
| 283 | , & ::example::member< vtable::inf1<T>, ::example::ft:: non_const > |
| 284 | ::template wrap < &T:: another_func > |
| 285 | }; |
| 286 | #endif |
| 287 | |
| 288 | // preprocessing code details |
| 289 | |
| 290 | // iterate all of the interface's members and invoke a macro (prefixed with |
| 291 | // BOOST_EXAMPLE_INTERFACE_) |
| 292 | #define BOOST_EXAMPLE_INTERFACE__MEMBERS(seq,macro) \ |
| 293 | BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), \ |
| 294 | BOOST_EXAMPLE_INTERFACE__ ## macro,seq) |
| 295 | |
| 296 | // extract signature sequence from entry |
| 297 | #define BOOST_EXAMPLE_INTERFACE__VTABLE(z,i,seq) \ |
| 298 | BOOST_EXAMPLE_INTERFACE__VTABLE_I(z,i, \ |
| 299 | BOOST_PP_TUPLE_ELEM(3,1,BOOST_PP_SEQ_ELEM(i,seq))) |
| 300 | |
| 301 | // split the signature sequence result/params and insert T at the beginning of |
| 302 | // the params part |
| 303 | #define BOOST_EXAMPLE_INTERFACE__VTABLE_I(z,i,seq) \ |
| 304 | BOOST_EXAMPLE_INTERFACE__VTABLE_II(z,i, \ |
| 305 | BOOST_PP_SEQ_HEAD(seq),(T)BOOST_PP_SEQ_TAIL(seq)) |
| 306 | |
| 307 | // emit the meta information structure and function pointer declaration |
| 308 | #define BOOST_EXAMPLE_INTERFACE__VTABLE_II(z,i,result_type,param_types) \ |
| 309 | template<typename T = void*> \ |
| 310 | struct BOOST_PP_CAT(inf,i) \ |
| 311 | { \ |
| 312 | typedef result_type result; \ |
| 313 | typedef ::boost::mpl::vector< BOOST_PP_SEQ_ENUM(param_types) > params; \ |
| 314 | }; \ |
| 315 | ::example::vtable_entry<BOOST_PP_CAT(inf,i)<> >::type BOOST_PP_CAT(func,i); |
| 316 | |
| 317 | // extract tuple entry from sequence and precalculate the name of the function |
| 318 | // pointer variable |
| 319 | #define BOOST_EXAMPLE_INTERFACE__INIT_VTABLE(z,i,seq) \ |
| 320 | BOOST_EXAMPLE_INTERFACE__INIT_VTABLE_I(i,seq,BOOST_PP_CAT(func,i), \ |
| 321 | BOOST_PP_SEQ_ELEM(i,seq)) |
| 322 | |
| 323 | // emit a function pointer expression that encapsulates the corresponding |
| 324 | // member function of T |
| 325 | #define BOOST_EXAMPLE_INTERFACE__INIT_VTABLE_I(i,seq,func,desc) \ |
| 326 | BOOST_PP_COMMA_IF(i) & ::example::member< BOOST_PP_CAT(vtable::inf,i)<T>, \ |
| 327 | ::example::ft:: BOOST_PP_TUPLE_ELEM(3,2,desc) >::template wrap \ |
| 328 | < &T:: BOOST_PP_TUPLE_ELEM(3,0,desc) > |
| 329 | |
| 330 | // extract tuple entry from sequence |
| 331 | #define BOOST_EXAMPLE_INTERFACE__FUNCTION(z,i,seq) \ |
| 332 | BOOST_EXAMPLE_INTERFACE__FUNCTION_I(z,i,BOOST_PP_SEQ_ELEM(i,seq)) |
| 333 | |
| 334 | // precalculate function name, arity, name of meta info structure and cv- |
| 335 | // qualifiers |
| 336 | #define BOOST_EXAMPLE_INTERFACE__FUNCTION_I(z,i,desc) \ |
| 337 | BOOST_EXAMPLE_INTERFACE__FUNCTION_II(z,i, \ |
| 338 | BOOST_PP_TUPLE_ELEM(3,0,desc), \ |
| 339 | BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(BOOST_PP_TUPLE_ELEM(3,1,desc))), \ |
| 340 | BOOST_PP_CAT(vtable::inf,i)<>, \ |
| 341 | BOOST_PP_CAT(BOOST_EXAMPLE_INTERFACE___,BOOST_PP_TUPLE_ELEM(3,2,desc)) \ |
| 342 | ) |
| 343 | |
| 344 | // emit the definition for a member function of the interface |
| 345 | #define BOOST_EXAMPLE_INTERFACE__FUNCTION_II(z,i,name,arity,types,cv) \ |
| 346 | inline types ::result name \ |
| 347 | (BOOST_PP_ENUM_ ## z (arity,BOOST_EXAMPLE_INTERFACE__PARAM,types)) cv() \ |
| 348 | { \ |
| 349 | return ptr_vtable-> BOOST_PP_CAT(func,i)(ptr_that \ |
| 350 | BOOST_PP_ENUM_TRAILING_PARAMS_Z(z,arity,p)); \ |
| 351 | } |
| 352 | |
| 353 | // emit a parameter of the function definition |
| 354 | #define BOOST_EXAMPLE_INTERFACE__PARAM(z,j,types) \ |
| 355 | ::example::param<types,BOOST_PP_INC(j)>::type BOOST_PP_CAT(p,j) |
| 356 | |
| 357 | // helper macros to map 'const_qualified' to 'const' an 'non_const' to '' |
| 358 | #define BOOST_EXAMPLE_INTERFACE___const_qualified BOOST_PP_IDENTITY(const) |
| 359 | #define BOOST_EXAMPLE_INTERFACE___non_const BOOST_PP_EMPTY |
| 360 | |
| 361 | |