I am trying to use a package function in my lisp library that will later be called from a C application. To be precise, it is shorten function from the str package.
Here is my lisp library text:
;; (require :str) ;; (use-package :str)
(defun echo-string (s) (coerce (format nil "Recieved input: ~10A" (shorten-string s)) 'base-string))
(defun shorten-string (s) (declare (string s)) (let ((l (length s))) (if (> l 10) (concat (shorten 7 s) (substring (- l 3) nil s)) s)))
First lines are commented out because i do not know their necessity in the library. That's part of the question.
However, all i get when trying to use any function from this library is a number of undecipherable errors, like this:
Here is my C code for a minimal case:
#include <stdlib.h> #include <stdio.h> #include <ecl/ecl.h>
// utility functions
cl_object make_cl_string(const char * pstring) { return ecl_make_constant_base_string(pstring, strlen(pstring)); }
int main(int argc, const char* argv[]) { /* Initialize ECL */ ecl_set_option(ECL_OPT_TRAP_SIGSEGV, false); cl_boot(argc, argv);
extern void init_lib_ASDF(cl_object); ecl_init_module(NULL, init_lib_ASDF);
// is this necessary? extern void init_lib_ECL_QUICKLISP(cl_object); ecl_init_module(NULL, init_lib_ECL_QUICKLISP); cl_object ecl_module_name = make_cl_string("ecl-quicklisp"); cl_require(1, ecl_module_name); cl_object str_module_name = make_cl_string("str"); cl_require(1, str_module_name); // this code fails, not clear what use as package designator /* cl_object str_package_name = make_cl_string(":str"); */ /* cl_use_package(1, str_package_name); */ extern void init_embedded_console(cl_object); ecl_init_module(NULL, init_embedded_console);
// library calls cl_object fname = make_cl_string("ECHO-STRING"); cl_object echo_func = cl_find_symbol(1, fname); assert(ECL_SYMBOLP(pstate->echo_func));
const char * line = "hello"; cl_object call_result = cl_funcall(2, echo_func, make_cl_string(line)); char * msg_str = malloc(call_result->string.fillp + 1); memcpy(msg_str, call_result->string.self, call_result->string.fillp + 1); printf("Output: %s\n", msg_str); free(msg_str);
const char * line2 = "hello"; call_result = cl_funcall(2, echo_func, make_cl_string(line2)); msg_str = malloc(call_result->string.fillp + 1); memcpy(msg_str, call_result->string.self, call_result->string.fillp + 1); printf("Output: %s\n", msg_str); free(msg_str);
return 0; }
What should be done for me to be able to use some package (installed with quicklisp) from my ECL library? How should the initialisation be done?
Le 05/07/2021 à 22:12, Иван Трусков a écrit :
What should be done for me to be able to use some package (installed with quicklisp) from my ECL library? How should the initialisation be done?
There are 3 questions:
- how to use packages? - how to use quicklisp? - what initialization is needed?
First about packages. A package is basically a triple: - a set of symbols, - a subset marked for "export", - a list of used packages. (plus some details for shadowed symbols and shadowing imports).
Symbols can be created independently from a package (eg. with MAKE-SYMBOL or with GENSYM), or they can be INTERN'ed in a package, ie. created having the package as home package, and belonging to the set of symbols of the package.
It is also possible to import symbols from one package into another package (so the sets of symbols of packages are not disjoint). In that case, the imported symbols keep their original home package. (If you import a non-interned symbol, then it's automatically interned, acquiring the package as home package).
When a package P uses another package Q, then the symbols exported by Q become visible in P (without being imported or interned).
The important invariant, is that in a given package, the name of the visible symbols (from packages used, imported or interned) must be unique. Therefore packages defined name spaces for symbols.
Two different symbols can have the same name, as long as they're interned in different packages, and they don't collide thru package use (export) or import.
Now, the main use of all this is with the lisp reader, which maintains a current package in CL:*PACKAGE*, that is used for two things:
- this is the package into which unqualified symbols are searched, and if not found: - this is the package into which new symbols are interned.
Note that CL:*PACKAGE* is not used to read qualified symbols. It is always possible to write lisp code using only qualified symbols, and thus becoming entirely independent from CL:*PACKAGE*. And even more, using :: instead of : you can use symbols independently from the export list of the package, since p::s reads the symbol named "S" in the package named "P", while p:s reads the symbol named "S" in the package named "P" only if S is exported by P.
(cl:defun cl-user::echo-string (cl-user::s) (cl:coerce (cl:format cl:nil "Received input: ~10A" (cl-user::shorten-string cl-user::s)) 'cl:base-string))
(cl:defun shorten-string (cl-user::s) (cl:declare (cl:string cl-user::s)) (cl:let ((cl-user::l (cl:length cl-user::s))) (cl:if (cl:> cl-user::l 10) (str:concat (str:shorten 7 cl-user::s) (cl:substring (- cl-user::l 3) cl:nil cl-user::s)) cl-user::s)))
Assuming the "STR" package exports "SHORTEN" and "CONCAT".
But it would be inconvenient to have to qualify all the symbols in lisp code (and some packages have quite long names…).
It will be more convenient, to read the following code, in a package that uses the "CL" package and the "STR" package:
(defpackage "MY-LIB" (:use "CL" "STR")) (in-package "MY-LIB")
(defun echo-string (s) (coerce (format nil "Received input: ~10A" (shorten-string s)) 'base-string))
(defun shorten-string (s) (declare (string s)) (let ((l (length s))) (if (> l 10) (concat (shorten 7 s) (substring (- l 3) nil s)) s)))
This answers the first question. Write your lisp code in your own package, that you will have carefully designed to use or import all the symbols you need to make it easy and clear to write your code.
Now there's the question of where does this "STR" package come from?
You seem to indicate that you use quicklisp to load it, and indeed there's a quicklisp system named "str" which defines a package named "STR" when loaded.
The question you need to answer is whether you want your program defer to run-time, on the user's workstation, when to use quicklisp to donwload and install the "str" system, and load it into your program to get the "STR" package and its functions? Do you realize that every month, new systems are added to quicklisp, but also SOME SYSTEMS are REMOVED from the quicklisp distribution!? Do you want your program to break next month because the quicklisp maintainer decided to remove some depdency from its distribution?
What if the user hasn't installed quicklisp?
What if he has installed quicklisp, but with a different version of the "str" system than the one you rely on?
I could give you the indication on how to do that, but this would clearly be a very bad idea.
What you want, is to obtain the "str" system one way or another; quicklisp may, today, be a good way to do that, but perhaps not. Perhaps you need a different version of the STR package (eg. you need to get the head from the git repository), or perhaps you need to make your own patches to it.
So I will only assume that you have the sources of the "str", and your own sources, "my-lib.lisp" around.
Now, what you should do, is to write an asd file "my-lib.asd" defining your own asdf system "my-lib" that will cite "str" as a dependency, and "my-lib.lisp" as a source file.
Then you will use the ecl-provided asdf functions that will load this system and all the dependencies listed, compile them and link them into your executable.
At run-time there will be no reference to and no use of quicklisp.
I'd start with "3.1.2.4 Build it as static library and use in C":
https://common-lisp.net/project/ecl/static/manual/System-building.html#Build...
and later move to shared libraries.
https://common-lisp.net/project/ecl/static/manual/System-building.html https://common-lisp.net/project/ecl/static/manual/System-building.html#Compi...
Now, in the C code, you should use find-symbol with two arguments. But if you more than a few functions in your library that you want to call from C, I would gather them in a vector or a list for easy access.
cl_object my_lib_pkg = cl_find_package(make_cl_string("MY-LIB")); if(my_lib_pkg==ECL_NIL){ perror("Package MY-LIB is absent"); exit(1); } cl_object echo_func = cl_find_symbol(2, make_cl_string("ECHO-STRING"),my_lib_pkg); if(echo_func==ECL_NIL){ perror("No symbol MY-LIB::ECHO-FUNC"); exit(1); }
If you have a lot of functions, you could do something like:
(defparameter *lib-api* #(initialize echo-string shorten-string … terminate))
struct { cl_object init; cl_object echo_string; cl_object shorten_string; … cl_object fini; } api;
cl_object lib_api = cl_symbol_value(cl_find_symbol(2, make_cl_string("*LIB-API*"),my_lib_pkg));
cl_fixnum i=-1; api.init=ecl_elt(lib_api,i++); api.echo_string=ecl_elt(lib_api,i++); api.shorten_string=ecl_elt(lib_api,i++); … api.fini=ecl_elt(lib_api,i++);
or even just keep a C vector of cl_object, and index it to retrieve the symbol of the function you want to call.