[Main page] [Overview]     /lpc /BASIC /chapter3

LPC Basics
Written by Descartes of Borg
23 april 1993


CHAPTER 3
Functions

By this point, you know that objects are made up of functions, that
the object interacts with the rest of the game through something called
calls to these functions, and you also know about variables and the
three simplest LPC data types void, int, and string. If you do not
fully understand this much, then you need either to review chapters 1
and 2, or mail descartes@nightmare or borg@hebron.connected.com because
either 1) You did not sufficiently read the first 2 chapters, or 2) I
have not written them clearly enough.

Like a function in math, an LPC function takes input and returns output.
Languages like Pascal distinguish between the concept of proceedure and
the concept of function. LPC does not. What Pascal refers to as a
proceedure, LPC calls a function of type void. That is, a function which
returns no value. The most trivial function would be the following:

void do_nothing() { }

This function accepts no input, performs no instructions, and returns no
value.

There are three parts to every properly written function:
1) The declaration
2) The definition
3) The call

Like with variables, functions must be declared. This will allow the
driver to know 1) what data type the function is returning and 2) how much
input, and of what type(s) the input is.
The more common word for input with respect to functions is "parameters",
and function input will from now on be referred to as the function's
parameters.
The definition is the code which tells the driver what that function
does with its input.
The call is a place in some other function which invokes the function.
You therefore might have the following bit of code:

void write_vals(); /* These are our declarations, usually */
int add(int x, int y); /* At the beginning of the object's code */

void write_vals() { /* The definition of the function */
int x; /* write_vals(). It writes the value of x */

x = add(2, 2); /* The call to add(). It assigns add()'s */
write(x+"\n"); /* return value to the variable x */
}

int add(int x, int y) { /* The definition of add(). It takes two */
return x + y; /* integers as input (its parameters), and */
} /* returns their sum to calling function */

Remember, that either write_vals() or add() could have come first in the
code. Order of functions in code is irrelevant. However, the declaration
of a function must come before the function's definition and before any
function which calls that function.

At this point, if I have been successful, you now understand what
structurally makes up any function. It is important that any time you
call any function, you understand what that return type is. If
you try assigning the return value of a function returning a string to
a variable declared as an int, you will end up with errors. If you
have played around at all with coding, you will notice that some
expressions you may have not understood in the past appear to be functions.
Things like: this_player(), write(), say(), this_object(), etc.
If you have made that observation, indeed you are right. In fact,
int write_vals() above, *two* function calls are made. The first is
to the function called add() which you defined. The second is a call
to a driver defined function called write(). The driver has already done
the declaration and definition of write() for you. You only need to
make calls to it. Functions like this one are called efuns, which is
short for everywhere function. They are defined in C in the game driver.
Since they are defined in C, efuns are executed faster than creator
defined functions. But the calls of efuns are used like calls to your
own functions, so it is important to know of efuns 1) What the efun's
return type is, and 2) what parameters it takes. To find this out,
most muds have one of the following:
most common: directory called /doc/efun where the declarations
and descriptions of the efuns are stored
also: a command called 'man' or 'help', where if you type
'man write' or 'help write', you will get the information
contained in the /doc/efun file for the efun in question
By looking it up, you will notice that write() is as follows:

void write(string|int)

This means that an appropriate call to write expects no return value and
passes as a single parameter which is either a string or an integer.

It is very important which order instructions are places within the
definition of functions. In write_vals() above, the instruction which
assigned the return value of add() to x must come before the instruction
which calls write() if you want to see the appropriate value used in
write. In other words, although functions may be placed in a file in any
order, inside function definitions, instruction lines must be placed
in the order in which they are to be executed.

One last thing about return values. The way you return a value to a
function which called the current function, you simply use the
instruction:
return value;
Where value is a variable or constant of any type matching the function's
declared return value type.

SUMMARY:
Files which define LPC objects are made up of functions. Functions are
made up of three parts: 1) The declaration, 2) The definition, and 3) The
call. Function definitions may appear inside the object file in any order
except in that you cannot define one function inside another function's
definition. Function declarations must come before the function's
definition *and* before the definition of any other function which makes a
call to that function. Function calls appear wherever they are needed.
The function definition is in turn made up of ordered lines of code
that begin with:
1) function return type
2) function name
3) parameter list
4) an opening { which states that function code begins here
5) instructions, expressions, and calls to other functions
6) a closing } stating that function code ends here
The trivial function would be:
void do_nothing() { }
Instructions (directives to the driver to do something), expressions(
things with values like 'a==b'), and function calls must appear in the
order in which you intend them to be executed.
Finally, any function whose return type is not void must return a value
of that function's return type.
Each game driver comes with a set of predeclared and defined functions
called efuns. You can find out what they are by checking out the
/doc/efun directory on most muds. Some muds have special wiz help or man
commands which allow creators easily to access this info. Calls to
efuns must obey the same rules as calls to creator defined functions.

NOTE:
Some drivers do not require function declarations. Some also do not
require that you state what the function's return type is. Regardless
of this fact, it is a bad idea to omit this information for the following
reasons:
1) People reading your code (including yourself at a later date) can
see what the hell it is you were trying to do
2) If you encounter errors involving type mismatching, it is easier to
debug.
3) It is plain considered good form.
Also, efuns vary from driver to driver, although they are still pretty
much standard. Nevertheless, there are also predefined, predeclared
functions called simul efuns (simulated efuns). These are very mudlib
dependent. They are functions which differ from efuns in that they
are defined in LPC in a special object called simul_efun.c. Any good
mudlib will have sufficiently documented its simul_efuns, but do not
expect simul_efuns to transfer from one mud to the next. Also, some
muds have moved some of their efuns from the driver to the mudlib
so now that they are simul_efuns. This actually makes no difference to
you as a creator, but I point this out because someone will tell me
that write() is not an efun on their mud, but instead a simul_efun.
If all goes well, write() will eventually be a simul_efun everywhere.