Search Top Index
HELP INLINE Jonathan Meyer, Sept 1990 Updated A.Sloman Oct 1990 LIB * DEFINE_INLINE Declares a new define form (see HELP *DEFINE_FORM) which simplifies the task of writing macros which look syntactically like calls to procedures, but generate code that is planted inline at compile time. Please note the WARNING below regarding use of inline macros. CONTENTS - (Use <ENTER> g to access required sections) -- Synopsis -- Writing inline macros -- Declarations -- Inline macros and include files -- Parameters -- Inline expressions -- Use of inline macros -- Example 1: Repeat Loops -- Example 2: Optional Compilation -- Related Documentation -- Synopsis ----------------------------------------------------------- define :inline <declarators> <name> (<parameters>, ...); <inline-expression> enddefine; -- Writing inline macros ---------------------------------------------- LIB *DEFINE_INLINE is very simple to use. Simply add the :inline expression to a procedure definition: define :inline MYPYTHAG(a,b); sqrt(a**2 + b**2) enddefine; MYPYTHAG(4,3) => ** 5.0 The define :inline works by constructing a new macro called MYPYTHAG which reads two comma separated expressions, and the substitutes them with a third expression sqrt(<expr1> ** 2 + <expr2> ** 2). define :inline is similar to the C pre-processor's '#define' directive. -- Declarations ------------------------------------------------------- Inline macros can be declared with any of the usual procedure declaration words: define :inline global TEST(a,b); a > b; enddefine; define :inline lconstant MIN(a,b); if a < b then a else b endif enddefine; There is no sense of an updater of an inline macro, so the following will produce a mishap: define :inline updaterof VAL(a,b); a -> b(1); enddefine; -- Inline macros and include files ------------------------------------ define_inline works with iconstant, allowing you to do: define :inline iconstant BAZ; enddefine; to define inline macros in include files. -- Parameters --------------------------------------------------------- Inline procedures take a variable number of parameters. When declaring a new inline macro, it should be noted that you cannot use the same name for two different parameters, so the following produces a mishap: define :inline SILLY(a,a); a enddefine; Also, parameters cannot also be declared as syntax words or other macros, so the following is illegal: define :inline SILLY(if, then); if * 2 enddefine; Inline macros can have 0 or more parameters. Like procedures, a ";" can be used to indicate that the inline macro has no parameters: define :inline REWRITE; 1001 enddefine; By default, parameters are assumed to represent Pop-11 expressions. However, you can specify a syntactic 'category' for any parameter, which causes the actual value of that parameter to be read with a particular procedure. A category is specified with = <category> following the parameter name: define :inline QUOTE(name=item); "name" enddefine; Specifying a category X for a parameter will mean that that parameter's actual value will be read with the procedure called "Xread", e.g. "itemread", "exprread". Standard categories for which there are corresponding autoloadable reader procedures are expr A Pop-11 expression (this is the default) item A single word, number, string, etc var An identifier name list A Pop-11 list expression typespec A <typespec> as defined in REF * DEFSTRUCT Note that a category other than the default "expr" should always be used where the actual value of a parameter will not be a Pop-11 expression. E.g, in the QUOTE example above, if "name" were not specified as category "item", then reading the actual value in QUOTE(hello) would cause "hello" to be interpreted as a reference to an identifier in an expression (and possibly cause a DECLARING VARIABLE message, etc). Categories are similiar to those used by LIB * FORM, q.v. -- Inline expressions ------------------------------------------------- An inline macro's expression is not compiled when the inline macro is defined, but instead inserted into the compilation stream when the macro is used. At this point, every occurrence of one of the parameters in the inline expression will be replaced by the textual items read for that parameter with the appropriate reader procedure. The macro is effectively a rewrite rule. This can cause problems. For example: define :inline FOO(a); a, a; enddefine; vars t = 1; define test(); t + 1 ->> t; enddefine; FOO(test()) => ** 2 3 In this situation, the call to test() was planted twice. If a programmer wishes to avoid this situation, the inline macro should use an lblock and an lvars variable to store the parameter: define :inline FOO(a); lblock lvars tmp = a; tmp,tmp; endlblock; enddefine; This definition will cause problems when you use the macro with: vars tmp = true; FOO(tmp) => Because FOO has its own definition of tmp, it will not be able to access the tmp that you have given as a parameter. The only way to avoid this situation is to use names within the macro that are unlikely to be used as variables. For example: define :inline FOO(a); lblock; lvars __foo_a = a; __foo_a * __foo_a => endlblock; enddefine; Note that an inline macro is not aware of the meaning of the text in its body. So every occurrence of an item which corresponds to a named parameter will be replaced: define :inline FOO(name=item); [this name is replaced - its read as an item and substituted] => 'Strings aren\'t substituted, so this name won\'t be replaced'=> enddefine; FOO('Jon') ** [this Jon is replaced - its read as an item and substituted] ** Strings aren't substituted, so this name won't be replaced Inline macro expressions can have many strange side-effects, but if you remember that the expression is simply rewritten using the given parameters you should avoid trouble. -- Use of inline macros ----------------------------------------------- Every occurrence of an inline macro will be replaced by a (possibly larger) expression in the compilation stream. Using inline macros can therefore increase program size. However, because there is some overhead in calling a procedure, it can be useful to write some functions as inline macros, reducing program execution time. There is therefore a tradeoff between program size and speed of execution. It is of course up to the programmer to weigh up this tradeoff and decide when inline macros should be used. Inline macros are especially useful for writing functions that evaluate some mathematical expression, or access a data structure. They can provide an invisible layer of abstraction between a datastructure and a program. Finally, inline macros are useful when you want to add debugging statements to a program. As an interesting example, we could define two versions of a printing routine, one which was an inline macro that did nothing, and another which actually performed some printing: #_IF DEF DEBUG define :inline PRINT_STATUS(status); [% caller(0), status %] => enddefine; #_ELSE define :inline PRINT_STATUS(s); enddefine; #_ENDIF WARNING: Inline macros look syntactically like calls to procedures, and although their use can be convenient and elegant they are potentially a source of confusion. Users reading a statement "foo(a,b,c)" will assume that foo is a procedure. Inline macros are not procedures, and attempting to pass an inline macro as a procedural argument will generally cause a mishap. It is common practice to denote macros and inline macros using an UPPER CASE name to reduce this ambiguity (as with all examples in this file). -- Example 1: Repeat Loops -------------------------------------------- Sometimes you want to repeatedly evaluate a simple expression until it returns true. In Pop-11, this is usually done using: repeat quitif(<expression>) endrepeat For example, suppose we wanted to descend down a list until we reach the last pair in the list. We could do this using: vars list = [1 2 3]; define last_pair(list); lvars list; repeat quitif((dest(list) -> list ->, tl(list) == [])); endrepeat; list; enddefine; This sort of construct is common enough that it is useful to define an inline macro for it. Lets call it REPEATUNTIL: define :inline global constant REPEATUNTIL(cond); repeat quitif(cond) endrepeat; enddefine; Now we can rewrite last_pair as follows: define last_pair(list); lvars list; REPEATUNTIL((dest(list) -> list ->, tl(list) == [])); list; enddefine; A similar construct, called REPEATWHILE, can be defined as follows: define :inline global constant REPEATWHILE(cond); repeat quitunless(cond) endrepeat; enddefine; This will repeatedly evaluate the expression while it returns a non-false value. -- Example 2: Optional Compilation ------------------------------------ The debugging macro shown above can be generalised. Here we have a macro which will evaluate its parameter only if OPTION_ON is defined: define :inline IF_NEEDED(a); #_IF DEF OPTION_ON a #_ENDIF enddefine; Note that the #_IF ... #_ENDIF expression is not evaluated when the inline macro is compiled. Instead, whenever the macro is used it will add the #_IF ... #_ENDIF statements to the front of the compiler stream (proglist). Try this: vars macro OPTION_ON = true; IF_NEEDED(npr('the OPTION_ON macro is on, so this is compiled')); but: syscancel("OPTION_ON"); IF_NEEDED('this is now ignored since OPTION_ON is not defined') Of course, this is useful in any situation where you repeat the same conditional compilation. It is worth noting the subtle difference between the two macros below: uses sysdefs #_IF DEF SUNOS define :inline SUNOS_ONLY(a) a enddefine; #_ELSE define :inline SUNOS_ONLY(a) enddefine #_ENDIF define :inline IF_SYS_V(a,b); #_IF DEF SYSTEM_V a #_ELSE b #_ENDIF enddefine; In the first case, SUNOS_ONLY, the #_IF expression is evaluated when the inline macro is made, so future changes to the SUNOS macro will not effect the definition of SUNOS_ONLY. In the second case, IF_SYS_V, the expression is evaluated not at compile time, but at each usage. Any changes to SYSTEM_V will effect the code planted by the IF_SYS_V macro. -- Related Documentation ---------------------------------------------- See also: HELP * MACRO - using Pop-11 macros HELP * DEFINE - defining new procedures HELP * DEFINE_FORM - writing new define forms REF * POPCOMPILE - Pop-11 compilation procdures REF * POPSYNTAX - Pop-11 syntax --- C.all/help/inline -------------------------------------------------- --- Copyright University of Sussex 1990. All rights reserved. ----------