chiark / gitweb /
3e5e8f72b9a21aa5cb546851dfc05e86b86477dc
[sod] / doc / intro.tex
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}
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
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
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
91 function @|f|, and a derived class @|D| which wants to \emph{extend} the
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
146   was necessary.  And you still end up with two copies of `B'.} %
147 The problem is that correctly composing behaviour from a collection of
148 superclasses requires knowledge of all of the superclasses involved and how
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.
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(); \}                                \\
167     virtual ~B() \{ \}                                        \-\\
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
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: