chiark / gitweb /
doc/intro.tex: Strengthen the argument about C++ a bit.
[sod] / doc / intro.tex
CommitLineData
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
28Sod is an object system for the C programming language.
29
30The 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
43Sod implements a fairly sophisticated object system, with multiple
44inheritance, but only single dispatch.
45
46\subsection{Ideology}
47
48Object systems tend to come with ideology attached, so Sod is no exception;
49but 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
70Sod's object system is significantly different in flavour\footnote{%
71 The pun was unintentional, but I'm happy with it.} %
72from most popular object-ish languages. Indeed, it bears far more similarity
73to Flavors, CLOS, and Dylan than to \Cplusplus, \Csharp, or Java (and still
74less to Go or Rust).
75
76Of the popular languages, \Cplusplus's object system is probably closest to
77Sod. Both are statically compiled, statically typed, implement single
78dispatch, and have relatively simple runtime metaprogramming facilities.
79\Cplusplus\ has a rich compile-time template metalanguage; Sod instead allows
80compile-time metaprogramming in Common Lisp, which is probably less
81convenient for simple cases but rather more pleasant for doing difficult
82things. Significantly, Sod has the @|SodObject| class, of which all other
83classes\footnote{%
84 Unless you construct a new root class of your own, which you can totally
85 do, but it's hard work.} %
86are subclasses; \Cplusplus\ has no such root class.
87
88Both systems provide multiple inheritance, but go about it very differently.
89The most important difference is that \Cplusplus\ provides only \emph{static
90 delegation}: if you have a class @|B| which defines some (virtual) member
756e9293 91function @|f|, and a derived class @|D| which wants to \emph{extend} the
ca8f5d55
MW
92behaviour of @|f| on instances of @|D|, then you must explicitly call @|B::f|
93at 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
109This works adequately when only single inheritance is involved. But if we
110now 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}
135The above prints
136\begin{prog}
137 B \\
138 X after \\
139 Y before \\
140 B
141\end{prog}
142which is unlikely to be what was wanted: `B' prints twice, and the `before'
143and `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
147The problem is that correctly composing behaviour from a collection of
148superclasses requires knowledge of all of the superclasses involved and how
4f94eb86
MW
149they're supposed to work together. Messing with virtual base classes has
150eliminated the problem of duplicating @|B|'s state, but has done nothing to
151help avoid duplicating @|B|'s \emph{behaviour} -- which is a shame, because
152duplicating one without the other is going to end badly.
ca8f5d55
MW
153
154The obvious workaround is to separate the functionality -- here, printing the
155messages -- from the plumbing, which arranges to do everything in the right
156order. You'd end up with a @|_f| member function in each class which wanted
157print something, and then every class would have a virtual @|f| which calls
158the 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}
189This is clearly much more cumbersome. Most disastrously, it spreads
190knowledge about how the various classes' contributions to the behaviour of
191@|f| fit together throughout the class graph. Also, even this approach is
192only suitable for especially simple cases. Suppose @|Y| needed to add
193behaviour before \emph{and} after @|B| -- maybe @|Y| is taking out a lock,
194and then releasing it. Then this approach won't work any more; indeed, it's
195hard to see any way to make this work without spreading knowledge about
196@|Y|'s lock everywhere.
197
198Compare 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}
220This prints
221\begin{prog}
222 Y before \\
223 B \\
224 X after
225\end{prog}
226as (I think) you'd hope. @|CALL_NEXT_METHOD| here does the job of figuring
227out what to do next, according to some rather complicated rules
228(described in full in \xref{sec:concepts.methods.combination}).
229
230Indeed, there's an even better way to write this particular case with Sod,
231because 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
257This 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: