Contents | < Browse | Browse >
17M. EC Library mode
--------------------

NOTE: there are quite a couple of things you need to know before you can
      rush off and create a library. please read this WHOLE chapter before
      proceeding!

EC can generate shared library files that can be called from any programming
language, and from any number of tasks at the same time.  Additionally it
supports almost all E language constructs in one way or another.

How?

You can create a library by writing a LIBRARY declaration at the top of the
sourcefile you want to become the library.  syntax:

	LIBRARY name,version,revision,versionstring IS function,...

for example:

	LIBRARY 'mytools.library',37,1,'MyTools 37.1 (2.7.95)' IS
	  opengui,closegui,...

The function names are simply those of the PROCs (no methods!) you wish to
make available as functions in the library.  The order in which they appear is
important, because it denotes at which offsets they will be in the library
(starting from -30). You can explicitly assign registers (e.g. `myfun(A0)').


A source with a declaration like this is in many respects similar to a normal
E source (i.e.  not a module).  You may also put names of PROCs from included
modules in the LIBRARY declaration.

If all went ok, EC will compile your source to two files:  the library file
(with the name from the LIBRARY declaration, not the source file!), and a
module (".library" replaced by ".m") which contains information for other E
programs on how to use your library (see
of procedures have their first char converted to uppercase.

You may now use your library from another E program as follows:

	MODULE '*mymodule'

	PROC main()
	  IF mymodulebase:=OpenLibrary('mymodule.library',37)
	    /* ... use libfunctions here! ... */
	    CloseLibrary(mymodulebase)
	  ENDIF
	ENDPROC

or similar.  This is all the E syntax need to create and use libraries,
however, there are quite a number of things one needs to know about what kind
of E code may be put into libraries.


What's so special about libraries then?

Well, imagine that the code in a library is completely separate from the
calling program, and that it may be executed at the same time by two different
tasks.  This weren't so problematic if it weren't for the fact that code in
most languages needs a global environment, not only for storing global
variables, but in E also for quite a lot of features including exception
handling, memory, io, library bases, etc.  (In E, the A4 register is used for
this).  It is safe to say that E code without this specific global environment
is almost impossible.

This environment can't be provided by the program that calls the library,
since this program might be written in C (even for E it would be a problem).
It can't be stored in the library base, since two tasks might be using the
library at the same time.  So what do E libraries do?  They create such an
environment for every task that calls them.  When the library is opened by a
task, a small E environment for that task is created.  When the task makes a
call, the environment belonging to the task is looked up (and put in A4).

In practise this means that libraries created by E should be opened/closed by
any one task EXACTLY ONCE, not more, not less (this shouldn't pose any
problems though).  If you put your library in the PD, you will want to put a
programmers notice in your docs mentioning this.  Strange things WILL happen
if you don't obey this rule.


features and problems/suggestions, hints and tips:
- If you use global variables, or in general the global environment, remember
  that you have a set of global vars for each task that uses your library.
  Global arrays are initialised as normal. (this is what you want, in
  general).
- A procedure main() still needs to be present. It will be called each time
  when a task makes an OpenLibrary() call to you library. Use it only to make
  minor initialisations, i.e. setting global vars to certain values, otherwise
  leave it empty. The code in main() is executed within a Forbid()/Permit().
  If the source for a LIBRARY contains a PROC close(), it will be called
  when the user of the library calls CloseLibrary() (i.e. it is the
  counterpart to main()). [close still mentioned as unref]
- Lists with variable bits in them, i.e [1,2,a].  A problem when >1 task uses
  the library.  This is a bit of a problem for example in taglists.  For now
  one should probably prefix those with a NEW and FastDisposeList() them
  afterwards (hopefully a better solution will be found sometime later).
- Exceptions can be raised and caught like normal, only NO exception should
  be thrown out of a library call EVER (strange things might happen). If one
  of your library calls uses code that might throw an exception, you have to
  handle it at the bottom level and turn it into an error code or somesuch.
- In general, and specifically with NEW, if you allocate resources in the
  library, free them in the library. If you feel you have to leave memory
  behind from or to the library, use AllocMem. Also, don't deallocate stuff
  that the calling task allocated for you.
- If you need callbacks, use hooks from the OS. Using pointers to PROCs
  like for example EasyGUI does won't do, as these then will be executed
  within the libraries' environment (i.e. the calling task will not be
  able to reach its own A4 (or equivalent in another language).
- LISP Cells make use of tricky garbage collection techniques on the stack,
  and generally will not work in libraries.  This could be made to work, I
  guess...
- stdin, stdout, conout, arg, wbmessage will all be NIL. If you wish to
  use for example WriteF(), it's best to open (and close!) your own console,
  and place its handle in stdout.
- libraries will stay in memory after use, so make you flush them regularly,
  otherwise debugging becomes particularly hard (you'll be using an old
  version: "didn't I just change that?"). Also close the library in all cases,
  otherwise you'll have to reboot to flush it. A trick to flush directly
  after testing the library is writing `AllocMem(100000000,0)' or similar
  directly after the CloseLibrary() call.
- memory allocation like New() and NEW, and other resources that E startup
  code may arrange, are freed when the calling task closes your library,
  i.e. not when your library is removed. Remember that your code might be
  dealing (hopefully transparently) with different sets of global variables,
  NEW memory pools, resources etc.
- If you want to use libraries with other languages like C, note that
  ShowModule has a "-c" option that shows a library module in .fd format,
  which can easily be converted to pragmas for a specific compiler.
- Do not "convert" old code to libraries without looking at it first,
  especially don't put code you don't have the source to in libraries
  without thinking first (it might have static lists, exceptions at weird
  spots etc.). (easygui.m is an example of this).
- Test your code under different circumstances. Have two programs use
  your library at the same time, and close in different orders to see
  wether it has problematic code in it. Try letting it fall over in any
  combination of EDBG, NILCHECK/S, enforcer/mungwall etc. Test your
  code first as normal exe or module before turning it into a library.
- In general, If you do tricky stuff involving the task or global environment,
  remember that you're in a different position from normal E code.

Have a look at the examples in the Src/Library dir.

It is currently not possible to create devices directly with EC library
link feature. If a lot of people require this feature I will build it in.