Commit | Line | Data |
---|---|---|
caa6f4b9 MW |
1 | %%% -*-latex-*- |
2 | %%% | |
3 | %%% Introduction to Sod and its object system | |
4 | %%% | |
5 | %%% (c) 2015 Straylight/Edgeware | |
6 | %%% | |
7 | ||
8 | %%%----- Licensing notice --------------------------------------------------- | |
9 | %%% | |
10 | %%% This file is part of the Sensible Object Design, an object system for C. | |
11 | %%% | |
12 | %%% SOD is free software; you can redistribute it and/or modify | |
13 | %%% it under the terms of the GNU General Public License as published by | |
14 | %%% the Free Software Foundation; either version 2 of the License, or | |
15 | %%% (at your option) any later version. | |
16 | %%% | |
17 | %%% SOD is distributed in the hope that it will be useful, | |
18 | %%% but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | %%% GNU General Public License for more details. | |
21 | %%% | |
22 | %%% You should have received a copy of the GNU General Public License | |
23 | %%% along with SOD; if not, write to the Free Software Foundation, | |
24 | %%% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
25 | ||
26 | \chapter{Introduction} \label{ch:intro} | |
27 | ||
28 | Sod is an object system for the C programming language. | |
29 | ||
30 | The software distribution for Sod contains two main parts: | |
31 | \begin{itemize} | |
cafa747a MW |
32 | \item a translator, or preprocessor, similar in spirit to \man{lex}{1} or |
33 | \man{yacc}{1}, which reads input files containing class definitions, and | |
34 | writes C source code and header files; and | |
caa6f4b9 MW |
35 | \item a very small runtime library, containing the built-in base classes and |
36 | some simple utility macros and functions for working with instances and | |
37 | classes. | |
38 | \end{itemize} | |
39 | ||
40 | %%%-------------------------------------------------------------------------- | |
41 | \section{About Sod's object system} \label{ch:intro.about} | |
42 | ||
43 | Sod implements a fairly sophisticated object system, with multiple | |
44 | inheritance, but only single dispatch. | |
45 | ||
46 | \subsection{Ideology} | |
47 | ||
48 | Object systems tend to come with ideology attached, so Sod is no exception; | |
49 | but Sod's ideology is different from that of most object systems. | |
50 | \begin{itemize} | |
51 | \item Sod provides an object system, not a module system. Sod provides no | |
52 | facilities for `information hiding'; there is no equivalent to the | |
53 | @|private| or @|protected| annotations in Java or \Cplusplus. The author | |
54 | takes the view (a) that such facilities are propertly part of a module | |
55 | system, and that trying to abuse classes so that they become modules is a | |
56 | mistake; and (b) that much useful functionality is unnecessarily hidden | |
57 | away behind abstract interfaces, and a gentle-ish nudge towards greater | |
58 | openness is called for. | |
59 | \item Sod's objective is to provide an effective tool for the expert | |
60 | programmer, in the classic `make easy things simple and, difficult things | |
61 | possible' mould. It isn't intended to be useful in an environment | |
62 | containing unassisted novice programmers. Sod tries to avoid placing | |
63 | technically unnecessary restrictions on programmers, and is likely to | |
64 | evolve in the direction of eliminating existing restrictions rather than | |
65 | growing new `safety' features. | |
66 | \end{itemize} | |
67 | ||
ca8f5d55 MW |
68 | \subsection{Comparison with other object systems} |
69 | ||
70 | Sod's object system is significantly different in flavour\footnote{% | |
71 | The pun was unintentional, but I'm happy with it.} % | |
72 | from most popular object-ish languages. Indeed, it bears far more similarity | |
73 | to Flavors, CLOS, and Dylan than to \Cplusplus, \Csharp, or Java (and still | |
74 | less to Go or Rust). | |
75 | ||
76 | Of the popular languages, \Cplusplus's object system is probably closest to | |
77 | Sod. Both are statically compiled, statically typed, implement single | |
78 | dispatch, and have relatively simple runtime metaprogramming facilities. | |
79 | \Cplusplus\ has a rich compile-time template metalanguage; Sod instead allows | |
80 | compile-time metaprogramming in Common Lisp, which is probably less | |
81 | convenient for simple cases but rather more pleasant for doing difficult | |
82 | things. Significantly, Sod has the @|SodObject| class, of which all other | |
83 | classes\footnote{% | |
84 | Unless you construct a new root class of your own, which you can totally | |
85 | do, but it's hard work.} % | |
86 | are subclasses; \Cplusplus\ has no such root class. | |
87 | ||
88 | Both systems provide multiple inheritance, but go about it very differently. | |
89 | The most important difference is that \Cplusplus\ provides only \emph{static | |
90 | delegation}: if you have a class @|B| which defines some (virtual) member | |
756e9293 | 91 | function @|f|, and a derived class @|D| which wants to \emph{extend} the |
ca8f5d55 MW |
92 | behaviour of @|f| on instances of @|D|, then you must explicitly call @|B::f| |
93 | at the appropriate point: | |
94 | \begin{prog} | |
95 | \#include <iostream> \\+ | |
96 | % | |
97 | class B \{ \\ | |
98 | public: \\ \ind | |
99 | virtual void f() \{ std::cout <{}< "B@\\n"; \} \\ | |
100 | virtual @~B() \{ \} \-\\ | |
101 | \}; \\+ | |
102 | % | |
103 | class D: public B \{ \\ | |
104 | public: \\ \ind | |
105 | void f() \{ B::f(); std::cout <{}< "D too@\\n"; \} \-\\ | |
106 | \}; | |
107 | \end{prog} | |
108 | ||
109 | This works adequately when only single inheritance is involved. But if we | |
110 | now introduce multiple inheritance, we see the problem. | |
111 | \begin{prog} | |
112 | \#include <iostream> \\+ | |
113 | % | |
114 | class B \{ \\ | |
115 | public: \\ \ind | |
116 | virtual void f() \{ std::cout <{}< "B@\\n"; \} \\ | |
117 | virtual @~B() \{ \} \-\\ | |
118 | \}; \\+ | |
119 | % | |
120 | class X: virtual public B \{ \\ | |
121 | public: \\ \ind | |
122 | void f() \{ B::f(); std::cout <{}< "X after@\\n"; \} \-\\ | |
123 | \}; \\+ | |
124 | % | |
125 | class Y: virtual public B \{ \\ | |
126 | public: \\ \ind | |
127 | void f() \{ std::cout <{}< "Y before@\\n"; B::f(); \} \-\\ | |
128 | \}; \\+ | |
129 | % | |
130 | class D: public X, public Y \{ \\ | |
131 | public: \\ \ind | |
132 | void f() \{ X::f(); Y::f(); \} // \comment{oh, dear} \-\\ | |
133 | \}; | |
134 | \end{prog} | |
135 | The above prints | |
136 | \begin{prog} | |
137 | B \\ | |
138 | X after \\ | |
139 | Y before \\ | |
140 | B | |
141 | \end{prog} | |
142 | which is unlikely to be what was wanted: `B' prints twice, and the `before' | |
143 | and `after' actions are both in the middle.\footnote{% | |
144 | Of course, one could have arranged to call @|Y::f| before @|X::f| -- but | |
145 | the important point is that one would have needed to \emph{know} that this | |
4f94eb86 | 146 | was necessary. And you still end up with two copies of `B'.} % |
ca8f5d55 MW |
147 | The problem is that correctly composing behaviour from a collection of |
148 | superclasses requires knowledge of all of the superclasses involved and how | |
4f94eb86 MW |
149 | they're supposed to work together. Messing with virtual base classes has |
150 | eliminated the problem of duplicating @|B|'s state, but has done nothing to | |
151 | help avoid duplicating @|B|'s \emph{behaviour} -- which is a shame, because | |
152 | duplicating one without the other is going to end badly. | |
ca8f5d55 MW |
153 | |
154 | The obvious workaround is to separate the functionality -- here, printing the | |
155 | messages -- from the plumbing, which arranges to do everything in the right | |
156 | order. You'd end up with a @|_f| member function in each class which wanted | |
157 | print something, and then every class would have a virtual @|f| which calls | |
158 | the various @|_f| functions in the right order, like this: | |
159 | \begin{prog} | |
160 | \#include <iostream> \\+ | |
161 | % | |
162 | class B \{ \\ | |
163 | protected: \\ \ind | |
164 | void _f() \{ std::cout <{}< "B@\\n"; \} \-\\ | |
165 | public: \\ \ind | |
166 | virtual void f() \{ _f(); \} \\ | |
054e8f8f | 167 | virtual @~B() \{ \} \-\\ |
ca8f5d55 MW |
168 | \}; \\+ |
169 | % | |
170 | class X: virtual public B \{ \\ | |
171 | protected: \\ \ind | |
172 | void _f() \{ std::cout <{}< "X after@\\n"; \} \-\\ | |
173 | public: \\ \ind | |
174 | void f() \{ B::_f(); _f(); \} \-\\ | |
175 | \}; \\+ | |
176 | % | |
177 | class Y: virtual public B \{ \\ | |
178 | protected: \\ \ind | |
179 | void _f() \{ std::cout <{}< "Y before@\\n"; \} \-\\ | |
180 | public: \\ \ind | |
181 | void f() \{ _f(); B::_f(); \} \-\\ | |
182 | \}; \\+ | |
183 | % | |
184 | class D: public X, public Y \{ \\ | |
185 | public: \\ \ind | |
186 | void f() \{ Y::_f(); B::_f(); X::_f(); \} \-\\ | |
187 | \}; | |
188 | \end{prog} | |
189 | This is clearly much more cumbersome. Most disastrously, it spreads | |
190 | knowledge about how the various classes' contributions to the behaviour of | |
191 | @|f| fit together throughout the class graph. Also, even this approach is | |
192 | only suitable for especially simple cases. Suppose @|Y| needed to add | |
193 | behaviour before \emph{and} after @|B| -- maybe @|Y| is taking out a lock, | |
194 | and then releasing it. Then this approach won't work any more; indeed, it's | |
195 | hard to see any way to make this work without spreading knowledge about | |
196 | @|Y|'s lock everywhere. | |
197 | ||
198 | Compare Sod's approach. | |
199 | \begin{prog} | |
200 | code c: includes \{ \\ | |
201 | \#include <stdio.h> \\- | |
202 | \#include <sod.h> \\ | |
203 | \#include "foo.h" \\ | |
204 | \} \\+ | |
205 | % | |
206 | class B: SodObject \{ \\ \ind | |
207 | void f() \{ puts("B"); \} \-\\ | |
208 | \} \\+ | |
209 | % | |
210 | class X: B \{ \\ \ind | |
211 | void b.f() \{ CALL_NEXT_METHOD; puts("X after"); \} \-\\ | |
212 | \} \\ | |
213 | % | |
214 | class Y: B \{ \\ \ind | |
215 | void b.f() \{ puts("Y before"); CALL_NEXT_METHOD; \} \-\\ | |
216 | \} \\+ | |
217 | % | |
218 | class D: X, Y \{ \} | |
219 | \end{prog} | |
220 | This prints | |
221 | \begin{prog} | |
222 | Y before \\ | |
223 | B \\ | |
224 | X after | |
225 | \end{prog} | |
226 | as (I think) you'd hope. @|CALL_NEXT_METHOD| here does the job of figuring | |
227 | out what to do next, according to some rather complicated rules | |
228 | (described in full in \xref{sec:concepts.methods.combination}). | |
229 | ||
230 | Indeed, there's an even better way to write this particular case with Sod, | |
231 | because Sod has dedicated \emph{method rĂ´les}. | |
232 | \begin{prog} | |
233 | code c: includes \{ \\ | |
234 | \#include <stdio.h> \\- | |
235 | \#include <sod.h> \\ | |
236 | \#include "foo.h" \\ | |
237 | \} \\+ | |
238 | % | |
239 | class B: SodObject \{ \\ \ind | |
240 | void f() \{ puts("B"); \} \-\\ | |
241 | \} \\+ | |
242 | % | |
243 | class X: B \{ \\ \ind | |
244 | [role = after] void b.f() \{ puts("X after"); \} \-\\ | |
245 | \} \\ | |
246 | % | |
247 | class Y: B \{ \\ \ind | |
248 | [role = before] void b.f() \{ puts("Y before"); \} \-\\ | |
249 | \} \\+ | |
250 | % | |
251 | class D: X, Y \{ \} | |
252 | \end{prog} | |
253 | ||
caa6f4b9 MW |
254 | %%%-------------------------------------------------------------------------- |
255 | \section{About this manual} | |
256 | ||
257 | This manual intends to provide complete documentation about Sod. | |
258 | ||
259 | %%%----- That's all, folks -------------------------------------------------- | |
260 | ||
261 | %%% Local variables: | |
262 | %%% mode: LaTeX | |
263 | %%% TeX-master: "sod.tex" | |
264 | %%% TeX-PDF-mode: t | |
265 | %%% End: |