inweb-bootstrap/docs/foundation-module/2-mth.html

218 lines
24 KiB
HTML
Raw Normal View History

2019-02-04 22:26:45 +00:00
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>2/wal</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body>
2019-02-09 12:33:40 +00:00
<!--Weave of '2/mth' generated by 7-->
2019-02-04 22:26:45 +00:00
<ul class="crumbs"><li><a href="../webs.html">&#9733;</a></li><li><a href="index.html">foundation</a></li><li><a href="index.html#2">Chapter 2: Memory, Streams and Collections</a></li><li><b>Methods</b></li></ul><p class="purpose">General support for something approximating method calls.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Method sets</a></li><li><a href="#SP4">&#167;4. Declaring methods</a></li><li><a href="#SP6">&#167;6. Adding methods</a></li><li><a href="#SP7">&#167;7. Calling methods</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Method sets. </b>This section provides a very rudimentary implementation of method calls,
ordinarily not available in C, but doesn't pretend to offer the full
functionality of an object-oriented language.
</p>
<p class="inwebparagraph">Instead, it's really intended for protocol-based coding patterns. Suppose that
we have objects of several different structure types, but all of them can
serve a given purpose &mdash; say, all of them contribute an adjective to the
Inform language. What we want is the ability to take a pointer, which might be
to an object of any of these types, and to tell the object to do something, or
ask it a question.
</p>
<p class="inwebparagraph">Alternatively, we may have a situation where there are multiple objects of the
same type which each represent a different way of doing something: for
example, in the Inweb source code, each different supported programming
language is represented by an object. These objects need to encapsulate all
the ways that one language differs from another, and they can do that by
providing "methods".
</p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. </b>The model is this. If a <code class="display"><span class="extract">typedef struct</span></code> definition includes the line
<code class="display"><span class="extract">METHOD_CALLS</span></code>, then any instance of that structure can have a queue of
tagged functions attached to it dynamically: those, we'll call "methods".
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">METHOD_CALLS</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">method_set</span><span class="plain"> *</span><span class="identifier">methods</span><span class="plain">;</span>
<span class="definitionkeyword">define</span> <span class="identifier">ENABLE_METHOD_CALLS</span><span class="plain">(</span><span class="identifier">obj</span><span class="plain">)</span>
<span class="identifier">obj</span><span class="plain">-&gt;</span><span class="identifier">methods</span><span class="plain"> = </span><span class="functiontext">Methods::new_set</span><span class="plain">();</span>
</pre>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. </b>A "method set" is simply a linked list of methods:
</p>
<pre class="display">
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">method_set</span><span class="plain"> {</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">method</span><span class="plain"> *</span><span class="identifier">first_method</span><span class="plain">;</span>
<span class="constant">MEMORY_MANAGEMENT</span>
<span class="plain">} </span><span class="reserved">method_set</span><span class="plain">;</span>
<span class="reserved">method_set</span><span class="plain"> *</span><span class="functiontext">Methods::new_set</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">method_set</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">method_set</span><span class="plain">);</span>
<span class="identifier">S</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Methods::new_set is used in <a href="#SP2">&#167;2</a>.</p>
<p class="endnote">The structure method_set is private to this section.</p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. Declaring methods. </b>Each method is a function, though we don't know its type &mdash; which is why we
resort to the desperate measure of storing it as a <code class="display"><span class="extract">void *</span></code> &mdash; with an ID
number attached to it. IDs should be from the <code class="display"><span class="extract">*_MTID</span></code> enumeration set.
</p>
<pre class="definitions">
<span class="definitionkeyword">enum</span> <span class="constant">UNUSED_METHOD_ID_MTID</span><span class="definitionkeyword"> from </span><span class="constant">1</span>
</pre>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>The type of a method must neverthess be specified, and we do it with one
of two macros: one for methods returning an integer, one for void methods,
i.e., those returning no value.
</p>
<p class="inwebparagraph">What these do is to use typedef to give the name <code class="display"><span class="extract">X_type</span></code> to the type of all
functions sharing the method ID <code class="display"><span class="extract">X</span></code>.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">IMETHOD_TYPE</span><span class="plain">(</span><span class="identifier">id</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">...)</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> (*</span><span class="identifier">id</span><span class="plain">##</span><span class="identifier">_type</span><span class="plain">)(</span><span class="identifier">args</span><span class="plain">);</span>
<span class="definitionkeyword">define</span> <span class="identifier">VMETHOD_TYPE</span><span class="plain">(</span><span class="identifier">id</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">...)</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">id</span><span class="plain">##</span><span class="identifier">_type</span><span class="plain">)(</span><span class="identifier">args</span><span class="plain">);</span>
</pre>
<pre class="display">
<span class="identifier">IMETHOD_TYPE</span><span class="plain">(</span><span class="constant">UNUSED_METHOD_ID_MTID</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">example</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">wont_be_used</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. Adding methods. </b>Provided a function has the right type for the ID we're using, we can now
attach it to an object with a method set, using the <code class="display"><span class="extract">METHOD_ADD</span></code> macro.
(If the type is wrong, the C compiler will throw errors here.)
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">METHOD_ADD</span><span class="plain">(</span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">id</span><span class="plain">, </span><span class="identifier">func</span><span class="plain">)</span>
<span class="functiontext">Methods::add</span><span class="plain">(</span><span class="identifier">upon</span><span class="plain">-&gt;</span><span class="identifier">methods</span><span class="plain">, </span><span class="identifier">id</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) &amp;</span><span class="identifier">func</span><span class="plain">);</span>
</pre>
<pre class="display">
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">method</span><span class="plain"> {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">method_id</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">method_function</span><span class="plain">;</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">method</span><span class="plain"> *</span><span class="identifier">next_method</span><span class="plain">;</span>
<span class="constant">MEMORY_MANAGEMENT</span>
<span class="plain">} </span><span class="reserved">method</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Methods::add</span><span class="plain">(</span><span class="reserved">method_set</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">ID</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">function</span><span class="plain">) {</span>
<span class="reserved">method</span><span class="plain"> *</span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">method</span><span class="plain">);</span>
<span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_id</span><span class="plain"> = </span><span class="identifier">ID</span><span class="plain">;</span>
<span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_function</span><span class="plain"> = </span><span class="identifier">function</span><span class="plain">;</span>
<span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">S</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain"> = </span><span class="identifier">M</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">method</span><span class="plain"> *</span><span class="identifier">existing</span><span class="plain"> = </span><span class="identifier">S</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> ((</span><span class="identifier">existing</span><span class="plain">) &amp;&amp; (</span><span class="identifier">existing</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain">)) </span><span class="identifier">existing</span><span class="plain"> = </span><span class="identifier">existing</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain">;</span>
<span class="identifier">existing</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain"> = </span><span class="identifier">M</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Methods::add appears nowhere else.</p>
<p class="endnote">The structure method is private to this section.</p>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. Calling methods. </b>Method calls are also done with a macro, but it has to come in four variants:
</p>
<p class="inwebparagraph"></p>
<ul class="items"><li>(a) <code class="display"><span class="extract">IMETHOD_CALL</span></code> for a method taking arguments and returning an <code class="display"><span class="extract">int</span></code>,
</li><li>(b) <code class="display"><span class="extract">IMETHOD_CALLV</span></code> for a method without arguments which returns an <code class="display"><span class="extract">int</span></code>,
</li><li>(c) <code class="display"><span class="extract">VMETHOD_CALL</span></code> for a method taking arguments and returning nothing,
</li><li>(d) <code class="display"><span class="extract">VMETHOD_CALLV</span></code> for a method without arguments which returns nothing.
</li></ul>
<p class="inwebparagraph">For example:
</p>
<p class="inwebparagraph"></p>
<pre class="display">
<span class="plain">IMETHOD_CALL(some_object, UNUSED_METHOD_ID_MTID, I"Hello", 17)</span>
</pre>
<p class="inwebparagraph">Note that it's entirely possible for the <code class="display"><span class="extract">upon</span></code> object to have multiple methods
added for the same ID &mdash; or none. In the <code class="display"><span class="extract">V</span></code> (void) cases, what we then do is
to call each of them in turn. In the <code class="display"><span class="extract">I</span></code> (int) cases, we call each in turn, but
stop the moment any of them returns something other than <code class="display"><span class="extract">FALSE</span></code>, and then
we put that value into the specified result variable <code class="display"><span class="extract">rval</span></code>.
</p>
<p class="inwebparagraph">If <code class="display"><span class="extract">some_object</span></code> has no methods for the given ID, then nothing happens, and
in the <code class="display"><span class="extract">I</span></code> case, the return value is <code class="display"><span class="extract">FALSE</span></code>.
</p>
<p class="inwebparagraph">It will, however, produce a compilation error if <code class="display"><span class="extract">some_object</span></code> is not a pointer
to a structure which has <code class="display"><span class="extract">METHOD_CALLS</span></code> as part of its definition.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">IMETHOD_CALL</span><span class="plain">(</span><span class="identifier">rval</span><span class="plain">, </span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">id</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">...) {</span>
<span class="identifier">rval</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">method</span><span class="plain"> *</span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">upon</span><span class="plain">?(</span><span class="identifier">upon</span><span class="plain">-&gt;</span><span class="identifier">methods</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain">):</span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">M</span><span class="plain">; </span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_id</span><span class="plain"> == </span><span class="identifier">id</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">method_rval_</span><span class="plain"> = (*((</span><span class="identifier">id</span><span class="plain">##</span><span class="identifier">_type</span><span class="plain">) (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_function</span><span class="plain">)))(</span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">method_rval_</span><span class="plain">) {</span>
<span class="identifier">rval</span><span class="plain"> = </span><span class="identifier">method_rval_</span><span class="plain">;</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="definitionkeyword">define</span> <span class="identifier">IMETHOD_CALLV</span><span class="plain">(</span><span class="identifier">rval</span><span class="plain">, </span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">id</span><span class="plain">) {</span>
<span class="identifier">rval</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">method</span><span class="plain"> *</span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">upon</span><span class="plain">?(</span><span class="identifier">upon</span><span class="plain">-&gt;</span><span class="identifier">methods</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain">):</span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">M</span><span class="plain">; </span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_id</span><span class="plain"> == </span><span class="identifier">id</span><span class="plain">) {</span>
2019-03-17 12:53:52 +00:00
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">method_rval_</span><span class="plain"> = (*((</span><span class="identifier">id</span><span class="plain">##</span><span class="identifier">_type</span><span class="plain">) (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_function</span><span class="plain">)))(</span><span class="identifier">upon</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">method_rval_</span><span class="plain">) {</span>
<span class="identifier">rval</span><span class="plain"> = </span><span class="identifier">method_rval_</span><span class="plain">;</span>
2019-02-04 22:26:45 +00:00
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="definitionkeyword">define</span> <span class="identifier">VMETHOD_CALL</span><span class="plain">(</span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">id</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">...)</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">method</span><span class="plain"> *</span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">upon</span><span class="plain">?(</span><span class="identifier">upon</span><span class="plain">-&gt;</span><span class="identifier">methods</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain">):</span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">M</span><span class="plain">; </span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_id</span><span class="plain"> == </span><span class="identifier">id</span><span class="plain">)</span>
<span class="plain">(*((</span><span class="identifier">id</span><span class="plain">##</span><span class="identifier">_type</span><span class="plain">) (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_function</span><span class="plain">)))(</span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">);</span>
<span class="definitionkeyword">define</span> <span class="identifier">VMETHOD_CALLV</span><span class="plain">(</span><span class="identifier">upon</span><span class="plain">, </span><span class="identifier">id</span><span class="plain">)</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">method</span><span class="plain"> *</span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">upon</span><span class="plain">?(</span><span class="identifier">upon</span><span class="plain">-&gt;</span><span class="identifier">methods</span><span class="plain">-</span><span class="element">&gt;first_method</span><span class="plain">):</span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">M</span><span class="plain">; </span><span class="identifier">M</span><span class="plain"> = </span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;next_method</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_id</span><span class="plain"> == </span><span class="identifier">id</span><span class="plain">)</span>
<span class="plain">(*((</span><span class="identifier">id</span><span class="plain">##</span><span class="identifier">_type</span><span class="plain">) (</span><span class="identifier">M</span><span class="plain">-</span><span class="element">&gt;method_function</span><span class="plain">)))(</span><span class="identifier">upon</span><span class="plain">);</span>
</pre>
2019-03-12 23:32:12 +00:00
<hr class="tocbar">
<ul class="toc"><li><a href="2-wal.html">Back to 'Writers and Loggers'</a></li><li><a href="2-llas.html">Continue with 'Linked Lists and Stacks'</a></li></ul><hr class="tocbar">
<!--End of weave-->
2019-02-04 22:26:45 +00:00
</body>
</html>