Arc Forumnew | comments | leaders | submitlogin
Racket ffi and anarki with shared libraries
2 points by cthammett 3178 days ago | 5 comments
Good Afternoon Ladies and Gents,

I was wondering if you could help me in using the Racket ffi to define a C structure and functions that operate on it. What is the correct process to define and use the structure? This is what I have so far:

I have create a header and c file, below i've only included the header.

  #ifndef STACKARRAY_H
  #define STACKARRAY_H

  #define MAX 10

  typedef struct _stackarray{
	int array[MAX];
	int size;
  } StackArray;

  void pushStackArray(StackArray *, int );
  int popStackArray(StackArray *);
  int fullStackArray(StackArray *);
  int emptyStackArray(StackArray *);
  StackArray * newStackArray();
  void displayStackArray(StackArray *);
  void delete(StackArray *);
  
  #endif

  To create the shared library file libStackArray.so I compiled with gcc:
  gcc -c -fPIC StackArray.c
  gcc -g -Wall -shared -o libStackArray.so StackArray.o

  Using anarki and the racket ffi should I write:

  ($:require ffi/unsafe
             ffi/unsafe/define
             ffi/unsafe/alloc)

  ($:define-ffi-definer define-StackArray (ffi-lib "/path/libStackArray"))

  ($:define-StackArray newStackArray (_fun -> _pointer ))
  ($:define-StackArray delete (_fun _pointer -> _void ))
  ($:define-StackArray pushStackArray (_fun _pointer _int -> _void))
  ($:define-StackArray popStackArray  (_fun _pointer  -> _int))
  ($:define-StackArray fullStackArray  (_fun _pointer -> _int))
  ($:define-StackArray emptyStackArray  (_fun _pointer  -> _int))
  ($:define-StackArray displayStackArray  (_fun _pointer -> _void))

  Then I would like to clean them up abit with macros in anarki:

  (mac newSA()
    `($:newStackArray))

  (mac deleteSA(stack)
    `($:delete ,stack))

  (mac pushSA(stack x)
    `($:pushStackArray ,stack ,x))

  (mac popSA(stack)
    `($:popStackArray ,stack))

  (mac fullSA?(stack)
    `($:fullStackArray ,stack))

  (mac emptySA?(stack)
    `($:emptyStackArray ,stack))

  (mac displaySA(stack)
    `($:displayStackArray ,stack))

  To use my StackArray I would like to write:
  (= stack (newSA))
  (pushSA stack 1)
  (pushSA stack 2)
  (fullSA? stack)
  (emptySA? stack)
  (displaySA stack)
  (popSA stack)
  (deleteSA stack)


2 points by cthammett 3177 days ago | link

Thank for the help, to summarise create .h and .c files:

  //StackArray.h

  #ifndef STACKARRAY_H
  #define STACKARRAY_H

  #define MAX 10

  typedef struct _stackarray{
	int array[MAX];
	int size;
  } StackArray;

  StackArray * newStackArray();
  int pushStackArray(StackArray *, int );
  int popStackArray(StackArray *);
  int fullStackArray(StackArray *);
  int emptyStackArray(StackArray *);
  void displayStackArray(StackArray *);
  void delete(StackArray *);
 
 #endif

  //StackArray.c

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include "StackArray.h"

  StackArray * newStackArray(){
	StackArray * stack = malloc(sizeof(StackArray));
	int i;
	for(i = 0; i < MAX; i++){
		stack->array[i] = 0;
	}
	stack->size = 0;
	return stack;
  }

  int pushStackArray(StackArray * stack, int item){
	if(stack->size < MAX){
		stack->array[stack->size] =  item;
		stack->size++;
		return item;
	} else {
		printf("The stack is full\n");
		return 0;
	}
  }

  int popStackArray(StackArray * stack){
	if(stack->size > 0){
		int top = stack->array[stack->size-1];
		stack->size--;
		return top;
	} else {
		return 0;
	}
  }

  int fullStackArray(StackArray * stack){
	return stack->size == MAX ? 1 : 0;
 }
  int emptyStackArray(StackArray * stack){
	return stack->size == 0 ? 1 : 0;
  }

  void displayStackArray(StackArray * stack){
	if(stack->size > 0){
		int i;
		for(i = 0; i < stack->size; i++){
			printf("%d ", stack->array[i]);
		}
		printf("\n");
	} else if (stack->size == 0) {
		printf("The stack is empty\n");
	}
  }

  void delete(StackArray * stack){
	free(stack);
  }

  Compile and create the Shared Library (Linux):
  gcc -c -fPIC StackArray.c
  gcc -g -Wall -shared -o libStackArray.so StackArray.o

  In Anarki type in:
  
  ($:require ffi/unsafe
    ffi/unsafe/define
    ffi/unsafe/alloc)

  ;Define the library
  ($:define-ffi-definer define-StackArray (ffi-lib "/home/conan/Documents/Arcprojects/ffi/stack/libStackArray"))

  ;Define the functions input and ouput

  ($:define-StackArray newStackArray (_fun -> _pointer ))
  ($:define-StackArray delete (_fun _pointer -> _void ))
  ($:define-StackArray pushStackArray (_fun _pointer _int -> _void))
  ($:define-StackArray popStackArray  (_fun _pointer  -> _int))
  ($:define-StackArray fullStackArray  (_fun _pointer -> _int))
  ($:define-StackArray emptyStackArray  (_fun _pointer  -> _int))
  ($:define-StackArray displayStackArray  (_fun _pointer -> _void))

  ;assign the functions a symbol to use in Anarki
  (= newSA $.newStackArray)
  (= deleteSA $.delete)
  (= pushSA $.pushStackArray)
  (= popSA $.popStackArray)
  (= fullSA? $.fullStackArray)
  (= emptySA? $.emptyStackArray)
  (= displaySA $.displayStackArray)
  
  ;To use the functions
  (= stack (newSA))
  (emptySA? stack) 
  (pushSA stack 1)
  (displaySA stack)
  (fullSA? stack)
  (emptySA? stack)
  (popSA stack)
  (deleteSA stack)

 ;A lispy solution using closures with very little effort... a macro can be used to clean up the middle.
  (def Stack ()
    (with (items nil acc 0)
      (fn msg
        (let it (car (cdr msg))	
          (case (car msg)
             set
              (=  acc (len it) items it )
            empty?
              (is items nil)
            push 
              (do (= acc (inc acc)) (= items (join items (list it))) )
            pop 
              (if (> acc 0) (do (= acc (inc acc -1)) (= items (butlast items))))
            peek
              items
            size	
               acc)))))
 
  (= stack (Stack))
  (stack 'empty?)
  (stack 'push 1)
  (stack 'peek)
  (stack 'pop)
  (stack 'set '(1 2 3 4 5 6 7 8 9 10))

-----

2 points by cthammett 3178 days ago | link

Thanks mate, below are the error codes:

  (= stack (newSA))
  #<cpointer>

   (pushSA stack 1)
  stack: undefined;
   cannot reference undefined identifier
    context...:
     /home/conan/Documents/anarki-master/ac.scm:1225:4

  (fullSA? stack)
    stack: undefined;
   cannot reference undefined identifier
    context...:
   /home/conan/Documents/anarki-master/ac.scm:1225:4

  (empytSA? stack)
  _empytSA?: undefined;
   cannot reference undefined identifier
    context...:
   /home/conan/Documents/anarki-master/ac.scm:1225:4

  (displaySA stack)
  stack: undefined;
   cannot reference undefined identifier
    context...:
   /home/conan/Documents/anarki-master/ac.scm:1225:4

  stack
  #<cpointer>

  Here is the C code:

  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include "StackArray.h"

  void pushStackArray(StackArray * stack, int item){
	if(stack->size < MAX){
		stack->array[stack->size] =  item;
		stack->size++;
	} else {
		printf("The stack is full\n");
	}
  }

  int popStackArray(StackArray * stack){
	if(stack->size > 0){
		stack->size--;
	} else if(stack->size == 0){
		printf("Stack array is empty\n");
	}
	return stack->array[stack->size + 1];
  }

  int fullStackArray(StackArray * stack){
	return stack->size == MAX ? 1 : 0;
  }
  int emptyStackArray(StackArray * stack){
	return stack->size == 0 ? 1 : 0;
  }

  StackArray * newStackArray(){
	StackArray * stack = malloc(sizeof(StackArray));
	int i;
	for(i = 0; i < MAX; i++){
		stack->array[i] = 0;
	}
	stack->size = 0;
	return stack;
  }

  void displayStackArray(StackArray * stack){
	int i;
	for(i = 0; i < stack->size; i++){
		printf("%d ", stack->array[i]);
	}
	printf("\n");
  }

  void delete(StackArray * stack){
	free(stack); 
  }

-----

2 points by rocketnia 3178 days ago | link

Most of those errors are saying it can't find a global variable named "stack". That's because when you do (= stack (newSA)), you're creating a global variable called "stack" in Arc but it's called "_stack" in Racket. In your macros, you're generating Racket code that uses the Arc variable name, so it's looking for a "stack" Racket global that doesn't exist. You can potentially fix this in your macros... but why use macros when you already have functions that do what you want? :)

  (= newSA $.newStackArray)
  (= deleteSA $.delete)
  (= pushSA $.pushStackArray)
  (= popSA $.popStackArray)
  (= fullSA? $.fullStackArray)
  (= emptySA? $.emptyStackArray)
  (= displaySA $.displayStackArray)
If you absolutely need macros, then here's a fixed version of pushSA that embeds its arguments as Arc expressions rather than Racket expressions:

  (mac pushSA (stack x)
    `( ($:lambda (stack x)
         (pushStackArray stack x))
       ,stack ,x))
  ; or...
  (mac pushSA (stack x)
    `($.pushStackArray ,stack ,x))
Fixing the others would be similar, but I picked pushSA as an example because it has two arguments.

Finally, I think this line just has a simple typo:

  typo:  (emptytSA? stack)
  fix:   (emptySA? stack)
How far does this get you? I haven't tried your code, and I don't know if this will fix all your problems, but maybe it's a start!

-----

3 points by cthammett 3177 days ago | link

Hey thanks this works great. I just need to fix an easy bug in the C code for pop.

-----

1 point by akkartik 3178 days ago | link

Can you share the error you get and the c file so I can try to reproduce?

-----