blob: cadf740d2261ad3bbe2a223e1c08e62928abd25d [file] [log] [blame]
Brian Silverman57be3162018-08-04 23:36:33 -07001
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
104namespace 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
210BOOST_EXAMPLE_INTERFACE( interface_x,
211 (( a_func, (void)(int), const_qualified ))
212 (( another_func, (int), non_const ))
213);
214
215// expands to:
216class 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
251public:
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
275template<typename T>
276interface_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