| 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: |