Loadfuncpp

How to Write External Functions and Libraries
for The Icon Programming Language in C++

Carl Sturtivant, June 2008, version 0.1alpha

Contents

Summary

Since 1996 a new function for Version 9 of Icon could be written in C following a certain interface, and compiled into a shared library, where such is a shared object (.so) under Unix-like operating systems. More recently this has been implemented using dynamically linked libraries (DLLs) under cygwin. The library could then be dynamically loaded by an Icon program calling the built-in function loadfunc which is passed the location and name of the library and the name of the C function desired, and which returns an Icon function that can subsequently be called. A suite of useful examples of this technique is a part of the distribution of Icon.

Writing a significantly complex external function for use by loadfunc is potentially difficult for two reasons. First, an Icon structure (or other value, string, list, set, table, etcetera) referred to solely by variables inside external code could be garbage collected by Icon. Second, working directly with Icon data more complex than numbers, strings and files requires a thorough understanding of the implementation of Icon. The Icon runtime system is implemented in an extension of C that is automatically translated into C. The design of the Icon virtual machine is not object oriented, and contains a great deal of straight-line code. Icon structures are operated upon as combinations of complex linked blocks. Writing code to work directly with such is lengthy, error prone and time consuming.

Loadfuncpp is a tool that makes writing external functions for Icon a relatively simple matter, requiring very little understanding of the implementation of the Icon virtual machine. Loadfuncpp exploits the close compatibility of C and C++ to provide a clean abstract interface to Icon. External functions for Icon are declared with C linkage, and the Icon virtual machine requires no modification to use exernal functions written using loadfuncpp.

Beginning C++ programmers with programming experience in other languages should have little difficulty with using loadfuncpp. It is not necessary to use templates, exceptions, or RTTI to use loadfuncpp. Little beyond some C experience plus how to define a simple class with virtual and non-virtual member functions is needed to use loadfuncpp. So C programmers with OOP experience but without C++ experience will also find loadfuncpp not difficult to use.

Loadfuncpp makes extensive use of operator overloading and other techniques to provide in C++ essentially the same suite of operations, functions and capabilities that are available to the Icon programmer in Icon. The use of these facilities in C++ is at most an order of magnitude more difficult than the corresponding Icon, and is often much easier than that. These facilities include the ability to write external functions that suspend a sequence of results, and the ability to call an Icon procedure that returns a value, which may in turn call a function that calls Icon recursively in the same fashion.

These facilities also include the ability to create, activate and refresh coexpressions, the ability to write external functions that are new string matching or string analyis functions, and the ability to work with all kinds of Icon data as if they were built-in types. Loadfuncpp also provides garbage collection safety as a matter of course, largely transparently to the C++ programmer. Loadfuncpp also provides a simple way to add new datatypes to Icon using the new external values added to Icon version 9.5 in 2008. These are used extensively by loadfuncpp, and so loadfuncpp cannot be used with versions of Icon prior to 9.5.

Loadfuncpp consists of two shared libraries (iload.so and iloadgpx.so) normally placed in the icon/bin directory (both are actually DLLs under cygwin despite the .so filename extension, and import libraries called iload.a and iloadgpx.a are used to link to them under cygwin) together with a small amount of Icon in loadfuncpp.icn, compiled into loadfuncpp.u1 and loadfuncpp.u2 (using 'icont -c loadfuncpp.icn') which are normally placed in the icon/lib directory. Loadfuncpp may then be used by an Icon program by adding the line 'link loadfuncpp' which makes the function loadfuncpp available to Icon.

The function loadfuncpp is used in place of loadfunc to dynamically load external functions written to use the loadfuncpp interface. The library containing loadfuncpp is itself loaded by an implicit call to loadfunc. The first call to loadfuncpp loads iload.so (and also loads iloadgpx.so if the Icon installation supports graphics) and replaces loadfuncpp by an external function in iload.so of the same name. This sequence of events makes the C++ interface in iload.so available to all libraries subsequently loaded by Icon through calls of loadfuncpp.

Installation

Installation of Loadfuncpp is in three parts. First ensuring a correct Icon installation. Second placing the loadfuncpp files appropriately. And third, ensuring that environment variables are set appropriately if the default locations of loadfuncpp files are not used.

Correct Icon Installation

Installing Icon version 9.4 is described here. However, you will need to install Icon version 9.5x from here for Loadfuncpp to run; you may follow the Icon version 9.4 instructions to install the 9.5x distribution. To verify you are running the correct version of Icon, use `icont -V` and `iconx -V`.

Default Placement of Loadfuncpp Files

Loadfuncpp consists of the following files. Currently (2008/6/27) loadfuncpp is available only as an experimental binary distribution. I am contemplating freely releasing the source once loadfuncpp has stabilized. The binary distribution contains the following files (iloadgpx.so is unimplemented in version 0.1alpha).

iload.so

C++ part of the loadfuncpp interface to iconx

loadfuncpp.icn

Icon part of the loadfuncpp interface to iconx

iloadgpx.so

additional C++ interface to Icon graphics facilities

loadfuncpp.h

C++ header for writing new external functions

The default installation of these files is as follows. (Here we assume that the directory containing your Icon installation is called icon.) I recommend that you use these locations unless there is a compelling reason not to.

iload.so

icon/bin

loadfuncpp.icn

icon/lib (compile using `icont -c loadfuncpp.icn`)

iloadgpx.so

icon/bin

loadfuncpp.h

wherever is convenient to #include in C++ source


Under cygwin only there are two additional files used when linking a dynamic library that uses loadfuncpp. These are the windows import libraries iload.a and iloadgpx.a, and are most naturally placed in the same directory as iload.so and iloadgpx.so (iloadgpx.so and iloadgpx.a are unimplemented in version 0.1alpha).

Alternative Placement of Loadfuncpp Files

Alternatively, you can place iload.so and iloadgpx.so anywhere you please and set the environment variable FPATH to include the directories containing iload.so and iloadgpx.so. FPATH should be a space or colon separated string of locations. You can compile loadfuncpp.icn using `icont -c loadfuncpp.icn` and place the resulting files (loadfuncpp.u1 and loadfuncpp.u2) in any directory and set the environment variable IPATH to include that directory. IPATH should also be a space or colon separated string of locations.

Loadfuncpp Installation Test

Once loadfuncpp is installed, you may test your installation by creating a small new exernal function and load and call it from Icon. Here's how.

  • Create a new directory, place a copy of loadfuncpp.h in it and work there
  • Edit a new file called (say) hello.cpp to contain the following code

    #include "loadfuncpp.h"
    
    extern "C" int hello(value argv[]) {
        argv[0] = "Hello World";
        return SUCCEEDED;
    }

  • Compile hello.cpp into a shared object hello.so using one of these compiler options
  • Edit a new file called (say) hello.icn to contain the following code and ensure that hello.so is in the same directory

    link loadfuncpp
    
    procedure main()
        hello := loadfuncpp("./hello.so", "hello", 0)
        write( hello() )
    end

  • Compile hello.icn by typing `icont hello.icn` and run it by typing `./hello` and you should get the output Hello World appearing in the console.

Manual

This manual assumes that you have a working installation of Loadfuncpp and Icon as described above. An installation of Icon alone is not sufficient, nor can Loadfuncpp be used with any Icon version prior to 9.5, as it relies upon the presence of external values which are first implemented as a part of that version.

Writing, Loading and Calling a new External Function

A new Icon external function written in C++ takes one of the following forms.

#include "loadfuncpp.h"

extern "C" int fixed_arity(value argv[]) {
    // ... has a fixed number of arguments
    return SUCCEEDED; //or FAILED
}

extern "C" int variable_arity(int argc, value argv[]){
    // ... has a variable number of arguments
    return SUCCEEDED; //or FAILED
}

The C++ type 'value' is an Icon value (called a descriptor), representing null or an integer, real, string, cset, list, table, set, file, procedure, coexpression, record, external value or an Icon variable. When such a function is called from Icon, its arguments are passed in the array argv starting from argv[1], and argv[0] is taken to be the value returned to Icon by the function. In the function variable_arity the number of arguments is also passed in argc. So the following is a one argument external function that returns its only argument.

#include "loadfuncpp.h"

extern "C" int ident(value argv[]) {
    argv[0] = argv[1];
    return SUCCEEDED;
}

The int returned to C++ is a signal to Icon indicating whether the call succeeded or failed. These are represented by the constants SUCCEEDED and FAILED respectively, defined in loadfuncpp.h. However there is also a simple mechanism in loadfuncpp to write external functions that suspend a sequence of values when called in Icon.

Functions compiled into a shared object are loaded into Icon by calls of loadfuncpp. Such calls indicate to Icon whether the loaded function has a variable or a fixed number of arguments, and if the latter, how many. For example the preceding functions might be loaded into Icon as follows if the body of fixed_arity was written to use two arguments.

link loadfuncpp

procedure main()
    fixed := loadfuncpp("./mylib.so", "fixed_arity", 2)
    variadic := loadfuncpp("./mylib.so", "variable_arity")
    #fixed and variadic now contain Icon functions
    #and may be treated like any other such values
end

If the number of arguments is not specified when loading a function of fixed arity then calling the result from Icon will lead to a memory violation. (Similar behavior will likely occur if a function of variable arity is loaded with a specific arity specified, or if too small an arity is specified for a fixed arity function.) Beware!

All of the C++ in this manual requires '#include "loadfuncpp.h"' and all of the Icon requires 'link loadfuncpp'. Hereafter this will be assumed implicitly.

Here is an external function of no arguments that returns null, represented in C++ by the constant nullvalue.

extern "C" int dull(value argv[]){
    argv[0] = nullvalue;
    return SUCCEEDED;
}

If this is compiled into the shared object 'dull.so' in the current directory then it might be called by Icon as follows.

dull := loadfuncpp("./dull.so", "dull", 0)
write(image( dull() ))

The value of argv[0] when an external function is called is of type procedure, and is the Icon value representing the external function being called. So failure to assign to argv[0] means that Icon loads a function that returns itself.

The C++ class value is intended to be used primarily in the interface to Icon. Icon structures in variables of this class are not safe from garbage collection. Icon does guarantee that argv[] is garbage collection safe however.

Working with Icon values

Variables of the C++ class local are intended to hold Icon values with guaranteed garbage collection safety. Only parameters and local variables should be declared of this class. Variables of the C++ class global should be used for static and global variables holding Icon values with guaranteed garbage collection safety. The interface to Icon is largely available through the class local. Most computation with Icon values in external functions may be implemented through use of the overloaded operators in this class, along with its member functions that represent additional Icon operators. Loadfuncpp also provides the Icon keywords and in the namespace 'Icon' provides a C++ variant of each of the built-in functions in Icon.

Assignment and Initialization among local, global and value

Assignment of a local to a local has the semantics of an Icon assignment. Specifically, if the left operand contains an Icon value that is an Icon variable (i.e. an Icon value used to refer to another Icon value so that the latter can be modified) then the assignment modifies the value referred to by that Icon variable, not the C++ variable whose value is the Icon variable.

Assignment is possible among the classes local, global and value, and aside from that between local and local has simple semantics. Initialization of variables of the classes local and global is possible from any of local, global and value, with the same simple semantics. In both cases the semantics is the same as Icon assignment, except in the case of an Icon variable, which is merely copied, so that the variable assigned or initialized now contains the same Icon variable. This lack of dereferencing is useful if an external function needs to return an Icon variable.

A variable of class local may also be initialized from an array of values as follows.

extern "C" int makelist(int argc, value argv[]){
    local arglist(argc, argv);
    argv[0] = arglist;
    return SUCCEEDED;
}

Such initialization creates an Icon list containing the values in the array starting from position 1. So the above function called from Icon returns a list of its arguments.

A variable of class local may be initialized by or assigned a C string, which causes an Icon string that is a copy of the original to be created, so that the original can safely be modified or destroyed later. If such copying is unwanted because the C string is a literal or constant, then the two argument value constructor may be used as follows.

extern "C" int f(value argv[]){
    local text = value(StringLiteral, "Hello");
    // ...
    return SUCCEEDED;
}

A variable of class local may also be initialized by or assigned a C++ long int causing the creation of an Icon integer.

Icon operations on variables of class local

Here is a table of the overloaded operators and member functions implementing Icon operators in the class local. These are listed with their Icon equivalents, and with a note of any restrictions or extensions. The unary ! operator in Icon is a generator and is supplied through loadfuncpp by other means.

local members for Icon operators

 

unary

Icon equivalent

 

*x

*x

 

~x

~x

 

-x

-x

 

++x

x +:= 1

 

--x

x -:= 1

 

binary

Icon equivalent

 

=

:=

 

+= -= *=

+:= -:= *:=

 

/= %= ^=

/:= %:= ^:=

 

+

+

 

-

-

 

*

*

 

/

/

 

%

%

 

x^y

x^y

 

x | y

x ++ y

 

x & y

x ** y

 

x && y

x -- y

 

x || y

x || y

 

|=

++:=

 

&=

**:=

 

==

===

 

!=

~===

 

< > <= >=

none

The comparison used when sorting

x[y]

x[y]

 

variadic

Icon Equivalent

 

x(...)

x(...)

Icon procedure call

(a,b ...)

[a,b ...]

Variadic list construction

member function

Icon equivalent

 

x.slice(y,z) 

x[y:z]

 

x.apply(y)

x ! y

Apply Icon procedure to arguments

x.listcat(y)

x ||| y

 

x.swap(y)

x :=: y

 

x.create()

create !x

 

x.create(y)

create x ! y

 

x.activate(y)

y@x

y defaults to &null

x.refresh()

^x

 

x.random()

?x

 

x.dereference()

.x

 

Icon Built-in Functions

All of the functions built in to Icon are available in C++ in the namespace 'Icon'. The C++ counterpart of an Icon built-in function returns &null if the original function would have failed. Those functions that are generators have been made to produce a single result. Those functions that are variadic have been made C++ compatible too; with a small number of arguments this can usually safely be ignored. The table below lists each C++ variant of each Icon function that is a generator, along with a comment indicating how it has been modified for C++ compatibility.

Function

 

bal

returns the first result generated only

find

returns the first result generated only

function

returns a list of the results originally generated

key

returns a list of the results originally generated

move

cannot be resumed

tab

cannot be resumed

upto

returns the first result generated only

Here is an example of the use of such Icon built-in functions in a new external function. The following function returns the set of its arguments.

extern "C" int makeset(int argc, value argv[]){
    local arglist(argc, argv);
    argv[0] = Icon::set(arglist);
    return SUCCEEDED;
}

Icon Keywords

All of the Icon keywords have been made available apart from &cset (to avoid a possible name collision), and &fail. The keywords are implemented through a keyword class with the unary '&' operator overloaded and are used thus in C++, as in the following example.

extern "C" int assignprog(value argv[]){
    local newname(argv[1]);
    &progname = newname; //Icon assignment semantics
    return FAILED;
}

The preceding function assigns a new value to the keyword &progname, just as in Icon. In all cases a keyword is used with the unary '&' operator, and therefore appears just as in an Icon program. The keywords that are generators in Icon produce a list of values in C++.

Types, Conversions and Errors

A well designed external function will probably do some type checking and conversions of its arguments, and perhaps give a run-time error if they are problematic.

The member function type() in the value or local classes returns one of the following constants indicating its Icon type: Null, Integer, BigInteger, Real, Cset, File, Procedure, Record, List, Set, Table, String, Constructor, Coexpression, External, or Variable. Constructor means a record constructor, and BigInteger is an integer with a binary representation larger than a machine word.

The member functions isNull() and notNull() in the value or local classes each return a boolean indicating whether or not the Icon type is null.

The member functions toInteger(), toReal(), toNumeric(), toString() and toCset() in the value or local classes, each endeavor to perform a conversion in place to the corresponding type following the same conventions as Icon. Each returns a boolean indicating whether the conversion succeeded. If the conversion failed, then the Icon value remains unchanged.

The function syserror(const char*) unconditionally and fatally terminates execution with an Icon style error message referring to the point of execution in Icon together with the one supplied as a C string argument. This nicely complements Icon::runerr.

Variadic Functions and Dynamic List Construction

Some built-in Icon functions take an arbitrary number of arguments. Unfortunately, C++ as of the present standard has no convenient way to define a function with an arbitrary number of arguments of the same type. So variadic functions included in the namespace 'Icon' such as writes are defined in two versions. The first has at most eight arguments, with defaults and glue code to account for fewer being supplied. This takes care of most uses of such functions.

The second uses a single argument of the class variadic, which is a wrapper for an Icon list of the arguments. The operator ',' (comma) has been overloaded so as to combine two locals into a variadic, and to combine a variadic and a local so as to append the local's value to the variadic's list. A variadic has a conversion to local that in effect removes the wrapper, and there are other sundry conversions and overloads of comma. These enable lists to be constructed in place, providing a syntactic equivalent of things like [x,y,z] in Icon, namely (x,y,z) in C++. The second implementation of writes may then be called as writes((x,y,z)). The second pair of parentheses is necessary as comma is not regarded as an operator by C++ when it is in a parameter list. Here is an example of the use of dynamic list construction.

extern "C" int divide(value argv[]){
    local x(argv[1]), y(argv[2]);
    argv[0] = (x / y, x % y);
    return SUCCEEDED;
}

Calling Icon from C++

The class local has overloaded the function call operator '()' so that a local may be called with function call syntax. If the value of the local is an Icon procedure (or function or record constructor) the effect is to call Icon from C++. There are two kinds of restrictions on these calls.

The first restriction is because C++ requires a specific arity when overloading the function call operator, and has no convenient way to handle an arbitrary number of parameters of the same type. This restriction is the same one affecting the calling of variadic functions, and is overcome in the same way with two implementations. One with a single argument of class variadic necessitating two pairs of parentheses when the call is made, and the other with up to eight arguments and useful for most procedure calls.

The second restriction is because there are three ways Icon can pass control back to a caller: by returning a value, by failing and by suspending a value. However, there is only one way for C++ to receive control back from a call it has made: by a value (possibly void) being returned. For this reason a call of an Icon procedure from C++ will return &null if the procedure fails, and will return rather than suspend if the procedure suspends a value. In either case, the call always returns cleanly with a single value. It is possible to iterate through the values suspended by an Icon procedure in C++ through a different mechanism.

Working with Generators from C++

Generators and the flow of control in Icon have no counterpart in C++. Nevertheless, it is useful to be able to both implement generators for Icon in C++, and iterate through generator sequences produced by Icon in C++, as well as create coexpressions in C++. All these facilities are provided by loadfuncpp.

Writing External Functions that are Generators

Here is an example of a generator function written in C++. It is a C++ implementation of the built-in Icon function seq, without the restriction to machine size integers.

class sequence: public generator {
    local current, inc;
  public:
    sequence(local start, local increment) {
        current = start - increment;
        inc = increment;
    }
    virtual bool hasNext() { 
        return true; 
    }
    virtual value giveNext() {
        return current += inc;
    }
};

extern "C" int seq2(value argv[]){
    sequence seq(argv[1], argv[2]);
    return seq.generate(argv);
}

This exemplifies all the features of loadfuncpp that enable generator functions to be written. First a C++ version of the generator is written as a class that inherits from the loadfuncpp class generator. Some data members are added to maintain state as generation occurs, and a constructor is written to initialize those data members. Finally the virtual functions hasNext() and giveNext() with exactly the above prototypes are overloaded. The sequence generated by an object of this class is defined to be that produced by repeatedly calling hasNext() to determine if there is a next member of the sequence, and if there is, calling giveNext() to get it.

Now the external function itself simply creates a generator object of the above class, presumably using values passed to it from Icon to initialize that object's state. Then the inherited member function generate is called, passing the original argument array for technical reasons, and the signal it returns is passed back to Icon. The effect of this call is to iterate through the calls of giveNext() while hasNext() returns true, suspending the results produced by each call of giveNext() to Icon. In a nutshell the call to generate suspends the sequence of results produced by the object to Icon. The reason that generate needs to be passed argv is that it needs to send its results to Icon by assigning to argv[0], in the same way that a single result is passed back.

Calling Icon Procedures that are Generators from C++

Here is an example of how to iterate over the results of a call of an Icon procedure. In the example the procedure to be called and its argument list are presumed to be the arguments passed to the external function, which then computes the sum of the first ten results suspended by the call, or the sum of all the results if less than ten results are computed.

class addup: public iterate {
  public:
    local total;
    int count;

    addup(): total(0), count(0) {}
	
    virtual void takeNext(const value& x) {
        total += x;
    }
    virtual bool wantNext(const value& x) {
        return ++count <= 10;
    }
};

extern "C" int sum10(value argv[]){
    addup sum;
    sum.every(argv[1], argv[2]);
    argv[0] = sum.total;
    return SUCCEEDED;
}

This exemplifies all the features of loadfuncpp that enable the results of a call to Icon to be iterated over in C++. First a class representing the loop that will iterate over the generator sequence is written, inheriting from the loadfuncpp class iterate. The data members of that class model the variables used in the loop, and the constructor models the initialization of those loop variables. It is convenient that these be public along with everything else; the class could be declared as a struct to achieve this. The two inherited virtual member functions wantNext() and takeNext() with exactly the above prototypes are then overridden. The function wantNext() models the loop condition: it returns true if the loop will process the next result produced by the generator, and false if the loop should be terminated. The function takeNext() models the loop body: it will be passed each result produced by the generator, and may modify the loop variables accordingly.

Now the external function itself simply creates an object of this class, using the constructor to initialize the loop variables, or simply assigning to them directly. This models setup code before the loop proper starts. Then the inherited member function every is called with the generator function and its argument list as arguments to the call. The call of every models executing the loop body by calling the generator function applied to its argument list and repeatedly alternately calling wantNext() to see if the loop should continue and takeNext() to pass the loop body the next result produced by the call to Icon. The loop is terminated either by wantNext() returning false or by the sequence of results generated by the call to Icon coming to an end, whichever occurs first.

Iterating over Exploded Structures in C++

This feature of loadfuncpp enables iteration over the results that would be generated in Icon by an expression of the form !x, with one important difference: if x is a table, then the results iterated over are those that would be produced by the Icon expression key(x). The technique use to perform such an iteration is almost identical to that used to iterate over the results of a call to an Icon procedure. The only difference is that a different inherited member function (bang) is called to run the iteration. Here is an example that sums the first ten elements of a list quite unnecessarily using this technique.

class addup: public iterate {
  public:
    local total;
    int count;

    addup(): total(0), count(0) {}

    virtual void takeNext(const value& x) {
        total += x;
    }
    virtual bool wantNext(const value& x) {
        return ++count <= 10;
    }
};

extern "C" int sumlist(value argv[]) {
    addup sum;
    sum.bang(argv[1]);
    argv[0] = sum.total;
    return SUCCEEDED;
}

Working with Coexpressions in C++

There are a handful of member functions in the class local that provide an essentially complete set of operations on coexpressions. These are straighforward to use and are summarized here.

local function

Icon equivalent

 

x.create()

create !x

 

x.create(y)

create x!y

 

x.activate(y)

y@x

y defaults to &null

x.refresh()

^x

 

Working with External Values

A new kind of external value is easily defined and used via inheritance from the loadfuncpp class external, which permanently hides the low level machinery of the C specification. Here is an example of such that illustrates the use of the available features.

class Widget: public external {
    long state;
  public:
    Widget(long x): state(x) {}
  	
    virtual value name() {
        return "Widget";
    }  	
    virtual external* copy() {
        return new Widget(state);
    }
    virtual value image() {
        char sbuf[100];
        sprintf(sbuf, "Widget_%ld(%ld)", id, state);
        return value(NewString, sbuf);
    }
    virtual long compare(external* ep) {
        //negative:less, zero:equal, positive:greater
        return this->state - ep->state;
    }  	
};

extern "C" int widget(value argv[]) {
    if( argv[1].type() != Integer ) return FAILED;
    argv[0] = new Widget(argv[1]);
    return SUCCEEDED;
}

extern "C" int widgetint(value argv[]) {
    if( argv[1].notExternal("Widget") ) return FAILED;
    // ...
    return SUCCEEDED;
}

The example defines an external function widget that returns an external value to Icon, and an external function widgetint that returns an integer extracted from a Widget to Icon. Of course a real library would have in addition a number of external functions to work with Widgets; these could call additional member functions in the Widget class to do the necessary work.

Overriding the inherited virtual functions name(), copy(), image() and compare() automatically redefines the behavior respectively of the built-in Icon functions type, copy and image and the Icon operators === and ~=== when applied to Widgets, as well as the order for sorting Widgets among themselves in Icon. Such overriding is optional, and the defaults defined in the C specification will apply otherwise. Specifically, the default copy is not to copy but to return the original.

There are automatic conversions to and from external* so that new widgets may be assigned to values or locals, and vice versa when appropriate. The operator new has been overloaded so that an external is allocated by Icon as a part of an Icon external block. The class external has a protected data member id that contains the serial number of the external value (assigned by Icon when it allocates the external block). Using id may be convenient when overriding the image() member function, as above.

External blocks are assumed by Icon not to contain any Icon descriptors, so do not declare any data members of the classes value, local or global when inheriting from external, unless you wish to invite disaster when a garbage collection occurs. Take into account that external blocks may be relocated or garbage collected by Icon. It is not possible to arrange for a destructor or anything else to be called when that occurs. If calling a destructor is essential, then place a pointer to the real object in the external object, and allocate and manage the real object yourself.

Dynamically Created Icon Procedures

This section will be added when the experimental implementation has stabilized, after loadfuncpp version 0.1alpha has been released.

Using Icon Records as Objects

A new procedure that is a copy of another with an Icon record bound to it may be created by calling the procedure bindself. The new procedure behaves exactly as the old one, except that a call of the procedure self from within it returns the record attached to it by bindself. This enables a record to contain a procedure that behaves like a method by virtue of being bound to it, as illustrated by the following example.

link loadfuncpp

record object(val, print)

procedure print()
    obj := self() | fail
    write( obj.val )
end

procedure newObject(x)
    obj := object(x) #don't assign print method yet
    #print will be a copy bound to the record it's embedded in
    obj.print := bindself(print, obj)
    return obj 
end

procedure main()
    obj := newObject("Hello")
    obj.print()
end

Note that self fails if called from a procedure that is not bound to a record i.e. one that has not been returned by bindself. It is possible to use bindself to bind a record to a procedure that already has a record bound to it. This simply replaces the bound record, which is useful for copying records that are to be treated as objects in this way, e.g. when copying a prototype object when simulating an object based inheritance scheme.

#