I'm writing a program/game where I use Guile for scripting on top of a C program.
For example I have a command struct type(which is also used for move_to) in C and corresponding
move_to
wrapper function to to create move_to commands in Guile. In the UI an input line can be entered
and is then executed in Guile.
For move_to
it look like this in C:
Some entity id is passed here as a SCM value, same for the new
the position and SCM value is passed for x and the same for y.
static SCM move_to(SCM scm_id, SCM x, SCM y) {
unsigned int id = scm_to_int(scm_id);
float new_x = scm_to_double(x);
float new_y = scm_to_double(y);
// Search for entity
int index = -1;
for (int i = 0; i < gs.entities.size(); i++) {
if (get_id(&(gs.entities[i])) == id) { index = i; break; }
}
command c = {new_x,new_y,MOVETO,0};
// Add move command to entity if one was found
if (index != -1) { printf("found index\n");add_command(&(gs.entities[index]), c); }
return SCM_UNSPECIFIED;
}
In this code the entity is searched in some entity list and if it exist, we
create a new move_to_command
and add it to the command list of the entity.
In the UI then e.g. (move_to 0 100 100)
can be entered and is then executed.
It's straightforward when creating a C function and wrapper when the function just returns an integer or nothing at all. But I'm not sure how to implement a function which returns a (C-/Guile-) struct value to Guile.
Let's say we have this struct type in C:
typedef struct {
unsigned int id;
event_type type;
unsigned int entity_id;
unsigned int by_entity;
} event;
How would I go to create a Guile struct value from this C struct which is returned when
when for example some Guile function - like 'get-next-event'
(which I have to write) is called. It would now also
be OK if I had to copy the value from the C struct to a new Guile struct over.
Saw that there is the function scm_make_struct(SCM vtable, SCM tail_size, SCM init_list)
,
but I am not sure how to create a vtable value which to pass here as a first argument.
I think older guile versions there was scm_make_vtable_vtable
.
Also I tried the C-function scm_struct_vtable
from the current version.
ADDENDUM:
SCM_API SCM scm_make_vtable (SCM fields, SCM printer);
Source Code Example:
#include "libguile.h"
static SCM give_me_100(void) {
return scm_from_int(100);
}
static SCM give_me_a_struct(void) {
SCM fields = scm_from_locale_string("pwpw");
SCM vtable = scm_make_vtable(fields,0);
SCM mystruct = scm_c_make_struct(vtable,0, 10, 20, SCM_UNDEFINED); //
return mystruct;
}
static void inner_main(void *closure, int argc, char **argv) {
/* preparation - make C functions callable from Guile */
scm_c_define_gsubr("give-me-100", 0, 0, 0, &give_me_100);
scm_c_define_gsubr("give-me-a-struct", 0, 0, 0, &give_me_a_struct);
scm_shell(argc, argv);
/* after exit */
}
int main(int argc, char **argv) {
scm_boot_guile(argc, argv, inner_main, 0);
return 0; /* never reached, see inner_main */
}
Which can be compiled with this line for example: gcc -o main -I/usr/include/guile/3.0 -lguile-3.0 main.c
Example output:
GNU Guile 3.0.8
Copyright (C) 1995-2021 Free Software Foundation, Inc.
Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.
Enter `,help' for help.
scheme@(guile-user)> (give-me-100)
$1 = 100
scheme@(guile-user)> (give-me-a-struct)
$2 = zsh: segmentation fault (core dumped) ./main
Thank you for any help and suggestions, Rael
To use and export C structs to Guile, one could do it like this - the example code is from here: https://github.com/agentlans/guile-foreign-example/blob/master/bessel.c
#include <math.h>
#include <libguile.h>
// Returns value of j0 Bessel function. This will be called from Guile Scheme.
SCM
j0_wrapper (SCM x)
{
return scm_from_double (j0 (scm_to_double (x)));
}
// A C struct that can hold any Guile Scheme object
struct foo
{
SCM x;
};
// Represents the foo type in Scheme
static SCM foo_type;
// Declarations needed for foo structs to be usable in Scheme
void init_foo_type()
{
SCM name = scm_from_utf8_symbol("foo");
SCM slots = scm_list_1(scm_from_utf8_symbol("x"));
foo_type = scm_make_foreign_object_type(name, slots, NULL); // NULL finalizer
}
// Creates a new foo struct containing the given Scheme object
SCM make_foo(SCM obj)
{
struct foo *foo_obj = scm_gc_malloc(sizeof(struct foo), "foo");
foo_obj->x = obj;
return scm_make_foreign_object_1(foo_type, foo_obj);
}
// Returns the Scheme object inside the foo struct
SCM get_foo(SCM foo_obj)
{
scm_assert_foreign_object_type(foo_type, foo_obj);
return ((struct foo *) scm_foreign_object_ref(foo_obj, 0))->x;
}
// Initializes the functions exposed in this Guile extension
void
init_bessel ()
{
scm_c_define_gsubr ("j0", 1, 0, 0, j0_wrapper);
init_foo_type();
scm_c_define_gsubr("make-foo", 1, 0, 0, make_foo);
scm_c_define_gsubr("get-foo", 1, 0, 0, get_foo);
}