|
iload.so |
C++ part of the loadfuncpp interface to iconx |
loadfuncpp.icn |
Icon part of the loadfuncpp interface to iconx |
iloadgpx.so |
C++ interface needed with the graphics build of Icon |
iloadnogpx.so |
C++ interface needed with the non-graphics build of Icon |
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 |
iload.a |
icon/bin (cygwin only) |
loadfuncpp.u1 |
icon/lib (from loadfuncpp.icn) |
loadfuncpp.u2 |
icon/lib (from loadfuncpp.icn) |
iloadgpx.so |
icon/bin |
iloadnogpx.so |
icon/bin |
loadfuncpp.h |
wherever is convenient to #include in C++ source |
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.
Once loadfuncpp is installed, you may test your installation by creating a small new external function and load and call it from Icon. Here's how.
#include "loadfuncpp.h" extern "C" int hello(value argv[]) { argv[0] = "Hello World"; return SUCCEEDED; } |
link loadfuncpp procedure main() hello := loadfuncpp("./hello.so", "hello", 0) write( hello() ) end |
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 endIf 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!
A relative or absolute path to the shared object may be used as the first argument to loadfuncpp, in which case loadfuncpp will look exactly where specified for it and nowhere else. Alternatively, just the filename of the shared object may be specified, in which case Icon will search FPATH for the file. If FPATH is not set in the environment Icon runs in, then iconx defines FPATH to consist of the current directory followed by the icon/bin directory. If FPATH is set in the environment Icon is run in, then iconx appends the icon/bin directory. In either case FPATH should be a space or colon separated series of directories, with no spaces in their paths. (This restriction will be cleaned up "soon".)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 safe are intended to hold Icon values with guaranteed garbage collection safety. The interface to Icon is largely available through the class safe. Most computation with Icon values in external functions may be implemented through use of the overloaded operators in using 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 safe and value
Assignment of a safe to a safe 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 the storage containing 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 safe and value, and has simple semantics: even values that are Icon variables are copied. Initialization of variables of the class safe is possible from any of safe 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, in the same way that an Icon procedure may.
A variable of class safe may also be initialized from an array of values as follows.
extern "C" int makelist(int argc, value argv[]){ safe 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 safe 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[]){ safe text = value(StringLiteral, "Hello"); // ... return SUCCEEDED; }A variable of class safe may also be initialized by or assigned a C++ long or int causing the creation of an Icon integer. Similarly initialization or assignment of a double causes the creation of an Icon real.
Icon operations on variables of class safe
Here is a table of the overloaded operators and member functions implementing Icon operators for the class safe. 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.
functions of safe 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
x[y]
x[y]
variadic
Icon Equivalent
x(...)
x(...)
(a,b ...)
[a,b ...]
member function
Icon equivalent
x.slice(y,z)
x[y:z]
x.apply(y)
x ! y
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[]){ safe 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[]){ safe 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 class 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 class 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 class each endeavors 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. These functions are intended for use with the arguments of an external function supplied to C++ before they are converted to the class safe and the real computation begins. (The use of these functions on the entries in argv[] is garbage-collection safe because Icon protects argv[].) For example to check that we have a string where we would need one as follows.
extern "C" int assignprog(value argv[]){ if( !argv[1].toString() ) { Icon::runerr(103, argv[1]); return FAILED; //in case &error is set } safe newname(argv[1]); &progname = newname; //Icon assignment semantics return FAILED; }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 error message supplied as a C string argument. This nicely complements Icon::runerr.
To avoid problems with C++ conversion/overloading ambiguities, the class safe has been provided with a conversion to the class value only, and no conversions to the types char*, int, long or double. On the other hand, the value class has such conversions and so an explicit conversion to value can be used in many contexts to permit an implicit conversion to a built-in type. See below for details.
The overloaded operators for the class safe defining much of Icon's repertoire in C++ have been defined outside the class safe, with the exception of those such as assignment, subscripting and call that C++ insists be non-static member functions, and almost all such as well as all other member functions have parameters of type safe only. This is so that the wide repertoire of conversions of other types to safe defined by loadfuncpp may be of maximum utility.
Conversions of char*, double, int and long to safe as well as value are defined, those from the built-in types creating copies on the Icon heap. Specifically, the conversion from char* to safe or to value assumes a null terminated C string, and produces a correspondingly copied Icon string.
Conversions of value to long and double have been defined. These behave as expected for Icon integers and reals respectively, but perform no conversions within Icon values (from integer to real or vice-versa).
There is also a conversion from value to char* defined. This does not make a C string, but rather simply produces a pointer to the start of an Icon string, which is not null terminated, and can move in the event of a garbage collection. If null termination is desired, then concatenate the loadfuncpp constant value nullchar before converting to char*, and if a copy outside of Icon is needed, then you will have to explicitly make one. Here is an example.
extern "C" int assignprog(value argv[]){ if( !argv[1].toString() ) { Icon::runerr(103, argv[1]); return FAILED; //in case &error is set } safe newname(argv[1]); char* s = value(newname || nullchar); //can move char sbuf[100]; sprintf(sbuf, "%s", s); //use the local copy sbuf //... }The non-member functions bytestointeger and integertobytes are useful to overtly convert to and from Icon integers of any size (i.e. type Integer or BigInteger behind the scenes). Both functions take a value and return a value. In this context Icon strings are considered to be representations of natural numbers. Each character is considered a base 256 digit in the obvious way, and the digits are defined to be in order from most to least significant. The empty string represents zero. bytestointeger takes such a string and produces the corresponding Icon integer. integertobytes takes an Icon integer and produces an Icon string representing its absolute value in the preceding sense. Neither function attempts type conversions, so for meaningful results they must be passed respectively a string value and an integer value.
The non-member functions base64, base64tointeger and base64tostring are useful to overtly convert strings and integers of any size to and from the commonly used base64 encoding. Each function takes a value and returns a value, and none attempts any type conversion of its arguments. base64 may be passed an Icon integer or string and produces a string containing the base64 encoding thereof. The sign of an integer is ignored, so the base64 encoding of its absolute value is produced. base64tointeger may be passed an Icon string that is a strict base64 encoding in which case it returns the corresponding Icon integer, and similarly base64tostring may be passed an Icon string that is a strict base64 encoding in which case it returns the corresponding Icon string. By strict base64 encoding is meant that the string's length is a multiple of four, that the end of the string is a sequence of between zero and two "=" characters (used to pad the file length to a multiple of four when encoding), and apart from that the remaining characters in the string are either lower or upper case letters, or digits, or the characters "/" and "+". Failure to supply a string containing a strict base64 encoding to either function will cause null to be returned.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 safe so as to append the safe's value to the variadic's list. A variadic has a conversion to safe 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[]){ safe x(argv[1]), y(argv[2]); argv[0] = (x / y, x % y); return SUCCEEDED; }Calling Icon from C++
The class safe has overloaded the function call operator '()' so that a safe may be called with function call syntax. If the value of the safe 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 { safe 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 just as 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: safe 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 by quite unnecessarily using this technique.
class addup: public iterate { public: safe 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 safe that provide an essentially complete set of operations on coexpressions. These are straightforward to use and are summarized here.
safe 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 Widget* wp = (Widget*)ep; return this->state - wp->state; } }; extern "C" int widget(value argv[]) { if( argv[1].type() != Integer ) { Icon::runerr(101, argv[1]); return FAILED; } argv[0] = new Widget(argv[1]); return SUCCEEDED; } extern "C" int widgetint(value argv[]) { if( argv[1].type() != External ) { Icon::runerr(131, argv[1]); return FAILED; } if( !argv[1].isExternal("Widget") ) { Icon::runerr(132, argv[1]); return FAILED; } external* ep = argv[1]; //implied conversion Widget* wp = (Widget*)ep; //can move if GC occurs! argv[0] = ep->state; 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 safes, 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 on the Icon heap. 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 or safe 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.
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() endNote 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.