Calling C Functions from Version 9 of Icon Ralph E. Griswold and Gregg M. Townsend Department of Computer Science, The University of Arizona 1.__Introduction Version 9 of Icon [1] supports calling C functions from Icon. In its simplest form, this facility can be used with only a lit- tle knowledge of how Icon is implemented. Sophisticated uses, however, require a good working knowledge of Icon data structures and Icon's internal operation [2-4] and RTL [5], the superset of C in which the Version 9 run-time system is written. There are two approaches to adding C functions to Icon. External functions can be added to the Icon interpreter; these functions are then available through the callout() interface to any Icon program that uses this customized version of Icon. Dynamic loading allows an Icon program to link a C function at execution time by calling loadfunc(); this approach has several advantages but is only available on a few platforms. 2.__External_Functions The Icon function callout(x0, x1, ..., xn) allows C functions to be called from Icon programs. The first argument, x0, desig- nates the C function to be called. The remaining arguments of callout() are supplied to the C function (possibly in modified form). In order to provide the necessary flexibility, callout() in turn calls a C function extcall(), which has the prototype dptr extcall(dptr argv, int argc, int *ip) where argv is a pointer to an array of descriptors containing the arguments, argc is the number of arguments, and ip is a pointer to an integer status code. The value returned by extcall() is a pointer to a descriptor if the computation is successful or NULL if it fails (which causes callout() to fail). A stub for extcall() is provided in extcall.r. This stub should be replaced by an appropriate C function, after which the Icon run-time system must be rebuilt. Although extcall() nor- mally is written entirely in C without the use of RTL constructs, it needs to be processed by rtt, the translator from RTL to C, to insure appropriate definitions and declarations are included. Designating_C_Functions The method of specifying C functions varies with system and application. A simple mechanism is to associate an integer with IPD240a - 1 - October 26, 1995 each function that can be called and use a C switch statement in extcall() to select the desired function. This method is used in the first example in Appendix 1. A better method is to use string names, as illustrated by the second function in that appendix. The C functions to be called must be linked with Icon (presumably through references in extcall()). Error_Handling The integer status code pointed to by ip is used for error handling. It is -1 when extcall() is called, indicating the absence of an error. If an error occurs in extcall(), the status code should be set to the number of an Icon run-time error [6]. Error 216 should be used if the designated C function is not found. If there is a descriptor associated with the error, a pointer to that descriptor should be returned by extcall(). If there is no specific descriptor associated with the error, extcall() should return NULL. See the examples in Appendix 1. If the status code is not -1 when extcall() returns, callout() terminates program execution with a run-time error message corresponding to the value of the status code. 3.__Dynamic_Loading On systems that support dynamic loading, the function loadfunc(libname, funcname) loads the C function funcname from the library libname and returns a procedure value. This value can then be used to call the function in the usual manner. To the Icon program, loaded functions appear similar to built-in functions. Appendix 2 presents an example of an Icon program with a dynamically loaded function. Functions loaded by Icon must provide a particular interface, described below, and so they are usually written specifically for use with Icon. Data structure definitions can be obtained by including the file src/h/rt.h that is distributed with the Icon source code. This file also declares macros and functions that may be useful for data conversion. C functions must be compiled and installed in a library before they can be loaded by an Icon program. The method for creating a library is system dependent. Here are examples for five particu- lar systems. SunOS 4.x: ld -o lib.so file1.o file2.o Solaris 2.x: cc -G -K pic -o lib.so file1.o file2.o Dec OSF1 v2.x:ld -shared -expect_unresolved '*' -o lib.so file1.o file2.o -lc SGI Irix 5.x: ld -shared -o lib.so file1.o file2.o FreeBSD: ld -Bshareable -o lib.so file1.o file2.o -lc These examples create a file named lib.so from the functions IPD240a - 2 - October 26, 1995 contained in file1.o and file2.o. The_C_Function_Interface A C function loaded by Icon has the prototype int funcname(int argc, dptr argv) where argc is the number of arguments and argv is an array of descriptors for the arguments. The first element argv[0] is not included in the count argc. It is used to return an Icon value, and is initialized to a descriptor for the null value. The actual arguments begin with argv[1]. If the C function returns zero, the call from Icon succeeds. A negative value indicates failure. If a positive value is returned, it is interpreted as an error number and a fatal error is signalled. In this case, if argv[0] has been changed, it is printed as the ``offending value''. There is no way for a C function to suspend, and no way to indicate a null value as an offending value in the case of an error. 4.__Data_Interface For either method of calling C from Icon, arguments to the C function are passed as Icon descriptors. The Icon run-time sys- tem contains type-checking and conversion facilities for the manipulation of descriptors. Some useful conversion functions are: cnv_int(dp1, dp2)Converts the value in the descriptor pointed to by dp1 to an integer descriptor pointed to by dp2, returning 0 if the conversion cannot be performed. Converts the value in the descriptor pointed to by dp1 to a string string descriptor (qualifier) pointed to by dp2, returning 0 if the conversion cannot be performed. Converts the value in the descriptor pointed to by dp1 to a real number descriptor (floating-point double) pointed to by dp2, returning 0 if the conversion fails. Some other useful macros and functions are: Qual(d)Tests if d is a descriptor for a string. IntVal(d)Accesses the (long) integer value of the integer descriptor d. Constructs a integer descriptor pointed to by dp from the (long) integer i. IPD240a - 3 - October 26, 1995 Accesses the length of the string in the descriptor d. Accesses the address of the string in the descriptor d. Constructs a C-style string from the descriptor pointed to by dp, placing it in sbuf, a buffer of length MaxCvtLen, if it is small enough or in the allocated string region if it is not. If there is not enough sapce available in the allocated string region, Error is returned. Copies the string of length i in sbuf to the allocated string region, returning NULL if the requested amount of space is not available. Places the floating-point double from the descriptor pointed to by dp into r. Conversion between Icon's structure values and C structs is more complicated and must be handled on a case-by-case basis. There are several global descriptors that may be useful in external functions: nulldesc descriptor for the null value zerodesc descriptor for the Icon integer 0 onedesc descriptor for the Icon integer 1 emptystr descriptor for the empty string See runtime/data.r for others. 5.__Acknowledgements The external function facilities described in Section 2 were were based on ones written by Bill Griswold, using earlier work of Andy Heron. The original implementation for Version 8.0 of Icon was done by Sandra Miller and the first author. Some of the material in this report was adapted from implementation notes provided by Bill Griswold. References 1. R. E. Griswold, C. L. Jeffery and G. M. Townsend, Version 9.1 of the Icon Programming Language, The Univ. of Arizona Icon Project Document IPD267, 1995. 2. R. E. Griswold and M. T. Griswold, The Implementation of the Icon Programming Language, Princeton University Press, 1986. 3. R. E. Griswold, Supplementary Information for the Implementation of Version 8 of Icon, The Univ. of Arizona Icon Project Document IPD112, 1995. IPD240a - 4 - October 26, 1995 4. R. E. Griswold, Supplementary Information for the Implementation of Version 9 of Icon, The Univ. of Arizona Icon Project Document IPD239, 1995. 5. K. Walker, The Run-Time Implementation Language for Icon, The Univ. of Arizona Icon Project Document IPD261, 1994. 6. R. E. Griswold and M. T. Griswold, The Icon Programming Language, Prentice-Hall, Inc., Englewood Cliffs, NJ, second edition, 1990. IPD240a - 5 - October 26, 1995 Appendix 1 - External Function Examples Example_1:_Functions_Designated_by_Numbers #if !COMPILER #ifdef ExternalFunctions /* * Example of calling C functions by integer codes. Here it's * one of three UNIX functions: * * 1: getpid (get process identification) * 2: getppid (get parent process identification) * 3: getpgrp (get process group) */ struct descrip retval; /* for returned value */ dptr extcall(dargv, argc, ip) dptr dargv; int argc; int *ip; { int retcode; int getpid(), getppid(), getpgrp(); if (!cnv_int(dargv, dargv)) {/* 1st argument must be a string */ *ip = 101; /* "integer expected" error number */ return dargv; /* return offending value */ } switch ((int)IntVal(*dargv)) { case 1: /* getpid */ retcode = getpid(); break; case 2: /* getppid */ retcode = getppid(); break; IPD240a - 6 - October 26, 1995 case 3: /* getpgrp */ if (argc < 2) { *ip = 205; /* no error number fits, really */ return NULL; /* no offending value */ } dargv++; /* get to next value */ if (!cnv_int(dargv, dargv)) {/* 2nd argument must be integer */ *ip = 101; /* "integer expected" error number */ return dargv; } retcode = getpgrp(IntVal(*dargv)); break; default: *ip = 216; /* external function not found */ return NULL; } MakeInt(retcode,&retval); /* make an Icon integer for result */ return &retval; } #else ExternalFunctions static char x; /* prevent empty module */ #endif /* ExternalFunctions */ #endif /* COMPILER */ Example_2:_Functions_Designated_by_Name #if !COMPILER #ifdef ExternalFunctions /* * Example of calling C functions by their names. Here it's just * chdir (change directory) or getwd (get path of current working directory). */ struct descrip retval; /* for returned value */ IPD240a - 7 - October 26, 1995 dptr extcall(dargv, argc, ip) dptr dargv; int argc; int *ip; { int len, retcode; int chdir(), getwd(); char sbuf[MaxCvtLen]; *ip = -1; /* anticipate error-free execution */ if (!cnv_str(dargv, dargv)) {/* 1st argument must be a string */ *ip = 103; /* "string expected" error number */ return dargv; /* return offending value */ } IPD240a - 8 - October 26, 1995 if (strncmp("chdir", StrLoc(*dargv), StrLen(*dargv)) == 0) { if (argc < 2) { /* must be a 2nd argument */ *ip = 103; /* no error number fits, really */ return NULL; /* no offending value */ } dargv++; /* get to next argument */ if (!cnv_str(dargv, dargv)) {/* 2nd argument must be a string */ *ip = 103; /* "string expected" error number */ return dargv; /* return offending value */ } qtos(dargv,sbuf); /* get C-style string in sbuf2 */ retcode = chdir(sbuf); /* try to change directory */ if (retcode == -1) /* see if chdir failed */ return (dptr)NULL; /* signal failure */ return &zerodesc; /* not a very useful result */ } else if (strncmp("getwd", StrLoc(*dargv), StrLen(*dargv)) == 0) { dargv++; /* get to next argument */ retcode = getwd(sbuf); /* get current working directory */ if (retcode == 0) /* see if getwd failed */ return NULL; /* signal failure */ len = strlen(sbuf); /* length of resulting string */ StrLoc(retval) = alcstr(sbuf,len); /* allocate and copy the string */ if (StrLoc(retval) == NULL) {/* allocation may fail */ *ip = 0; return (dptr)NULL; /* no offending value */ } StrLen(retval) = len; return &retval; /* return a pointer to the qualifier */ } else { *ip = 216; /* name is not one of those supported here */ return dargv; /* return pointer to offending value */ } } #else /* ExternalFunctions */ static char x; /* avoid empty module */ #endif /* ExternalFunctions */ #endif /* !COMPILER */ IPD240a - 9 - October 26, 1995 Appendix 2 - Dynamic Loading Example Icon_Program # Demonstrate dynamic loading of bitcount() function global bitcount procedure main() local i bitcount := loadfunc("./lib.so", "bitcount") every i := 500 to 520 do write(i, " ", bitcount(i)) end C_Function /* * bitcount(i) -- count the bits in an integer */ #include "rt.h" int bitcount(argc, argv) int argc; struct descrip *argv; { struct descrip d; unsigned long v; int n; if (argc < 1) return 101; /* integer expected */ if (!cnv_int(&argv[1], &d)) { argv[0] = argv[1]; /* offending value */ return 101; /* integer expected */ } IPD240a - 10 - October 26, 1995 v = IntVal(argv[1]); /* get value as unsigned long */ n = 0; while (v != 0) { /* while more bits to count */ n += v & 1; /* check low-order bit */ v >>= 1; /* shift off with zero-fill */ } MakeInt(n, &argv[0]); /* construct result integer */ return 0; /* success */ } IPD240a - 11 - October 26, 1995