Use another language to call a function

From Rosetta Code
Revision as of 20:12, 8 June 2017 by Kazinator (talk | contribs) (→‎Using cptr and memcpy: Show C side-by-side.)
Task
Use another language to call a function
You are encouraged to solve this task according to the task description, using any language you may know.
This task has been clarified. Its programming examples are in need of review to ensure that they still fit the requirements of the task.

This task is inverse to the task Call foreign language function. Consider the following C program: <lang c>#include <stdio.h>

extern int Query (char * Data, size_t * Length);

int main (int argc, char * argv []) {

  char     Buffer [1024];
  size_t   Size = sizeof (Buffer);
  
  if (0 == Query (Buffer, &Size))
  {
     printf ("failed to call Query\n");
  }
  else
  {
     char * Ptr = Buffer;
     while (Size-- > 0) putchar (*Ptr++);
     putchar ('\n');
  }

}</lang>

Implement the missing Query function in your language, and let this C program call it. The function should place the string Here am I into the buffer which is passed to it as the parameter Data. The buffer size in bytes is passed as the parameter Length. When there is no room in the buffer, Query shall return 0. Otherwise it overwrites the beginning of Buffer, sets the number of overwritten bytes into Length and returns 1.

Ada

The interface package Exported specification: <lang Ada>with Interfaces.C; use Interfaces.C; with Interfaces.C.Strings; use Interfaces.C.Strings;

package Exported is

  function Query (Data : chars_ptr; Size : access size_t)
     return int;
  pragma Export (C, Query, "Query");

end Exported;</lang> The package implementation: <lang Ada>package body Exported is

  function Query (Data : chars_ptr; Size : access size_t)
     return int is
     Result : char_array := "Here am I";
  begin
     if Size.all < Result'Length then
        return 0;
     else
        Update (Data, 0, Result);
        Size.all := Result'Length;
        return 1;
     end if;
  end Query;

end Exported;</lang> With GNAT it can be built as follows: <lang ada>gcc -c main.c gnatmake -c exported.adb gnatbind -n exported.ali gnatlink exported.ali main.o -o main</lang> Sample output:

Here am I

AutoHotkey

It is possible to register an autohotkey function as a callback and get a pointer to it using the builtin registercallback function. Care should be taken that the external language code is running in the same thread as autohotkey. This is not a problem when using dllcall to use the external language. To run an autohotkey function from an external program running in a different thread, you can use ahkFunction in AutoHotkey.dll From the documentation on registercallback: <lang AutoHotkey>; Example: The following is a working script that displays a summary of all top-level windows.

For performance and memory conservation, call RegisterCallback() only once for a given callback

if not EnumAddress  ; Fast-mode is okay because it will be called only from this thread:

   EnumAddress := RegisterCallback("EnumWindowsProc", "Fast")

DetectHiddenWindows On  ; Due to fast-mode, this setting will go into effect for the callback too.

Pass control to EnumWindows(), which calls the callback repeatedly

DllCall("EnumWindows", UInt, EnumAddress, UInt, 0) MsgBox %Output%  ; Display the information accumulated by the callback.

EnumWindowsProc(hwnd, lParam) {

   global Output
   WinGetTitle, title, ahk_id %hwnd%
   WinGetClass, class, ahk_id %hwnd%
   if title
       Output .= "HWND: " . hwnd . "`tTitle: " . title . "`tClass: " . class . "`n"
   return true  ; Tell EnumWindows() to continue until all windows have been enumerated.

}</lang>

C

I rewrote the driver as <lang c>#if 0 I rewrote the driver according to good sense, my style, and discussion.

This is file main.c on Autumn 2011 ubuntu linux release. The emacs compile command output:

-*- mode: compilation; default-directory: "/tmp/" -*- Compilation started at Mon Mar 12 20:25:27

make -k CFLAGS=-Wall main.o cc -Wall -c -o main.o main.c

Compilation finished at Mon Mar 12 20:25:27

  1. endif
  1. include <stdio.h>
  2. include <stdlib.h>

extern int Query(char *Data, unsigned *Length);

int main(int argc, char *argv[]) {

 char Buffer[1024], *pc;
 unsigned Size = sizeof(Buffer);
 if (!Query(Buffer, &Size))
   fputs("failed to call Query", stdout);
 else
   for (pc = Buffer; Size--; ++pc)
     putchar(*pc);
 putchar('\n');
 return EXIT_SUCCESS;

} </lang> With solution <lang c>

  1. if 0

This is file query.c

-*- mode: compilation; default-directory: "/tmp/" -*- Compilation started at Mon Mar 12 20:36:25

make -k CFLAGS=-Wall query.o cc -Wall -c -o query.o query.c

Compilation finished at Mon Mar 12 20:36:26

  1. endif
  1. include<string.h>

int Query(char *Data, unsigned *Length) {

 const char *message = "Here am I";
 unsigned n = strlen(message);
 if (n <= *Length)
   return strncpy(Data, message, (size_t)n), *Length = n, 1;
 return 0;

} </lang> And finally, excitement! <lang bash>$ gcc main.c query.o -o main && ./main Here am I $ </lang>

D

This shows how to perform the task on Windows. Elsewhere the procedure is very similar.

First write a D module like this, named "query_func.d":

<lang d>import core.stdc.string;

extern(C) bool query(char *data, size_t *length) pure nothrow {

   immutable text = "Here am I";
   if (*length < text.length) {
       *length = 0; // Also clears length.
       return false;
   } else {
       memcpy(data, text.ptr, text.length);
       *length = text.length;
       return true;
   }

}</lang>

Generate a library file with:

dmd -lib query_func.d

This generates a query_func.lib file.

Then create a C file named "mainc.c", given in the task description and here improved a little:

<lang c>#include <stdio.h>

  1. include <stdbool.h>

extern bool query(char *data, size_t *length);

int main() {

   char buffer[1024];
   size_t size = sizeof(buffer);
   if (query(buffer, &size))
       printf("%.*s\n", size, buffer);
   else
       puts("The call to query has failed.");
   return 0;

}</lang>

Then you can compile and link all with the DMC C compiler(on Linux you can use GCC):

dmc query_func.lib mainc.c

It generates the "mainc.exe" binary, that prints the desired output:

Here am I

Delphi

<lang delphi> function Query(Buffer: PChar; var Size: Int64): LongBool; const

   Text = 'Hello World!';

begin

   If not Assigned(Buffer) Then
   begin
       Size := 0;
       Result := False;
       Exit;
   end;
   If Size < Length(Text) Then
   begin
       Size := 0;
       Result := False;
       Exit;
   end;
   Size := Length(Text);
   Move(Text[1], Buffer^, Size);
   Result := True;

end; </lang>

To use this function from C you have to export this as a DLL and bind your C program to this function.

Fortran

Simple task because interoperability with C is in Fortran language since F2003 standard. <lang Fortran> !----------------------------------------------------------------------- !Function !----------------------------------------------------------------------- function fortran_query(data, length) result(answer) bind(c, name='Query')

  use, intrinsic  :: iso_c_binding, only: c_char, c_int, c_size_t, c_null_char
  implicit none
  character(len=1,kind=c_char), dimension(length),  intent(inout) ::  data
  integer(c_size_t), intent(inout) :: length
  integer(c_int) :: answer
  answer = 0
  if(length<10) return
  data = transfer("Here I am"//c_null_char, data)
  length = 10_c_size_t
  answer = 1

end function fortran_query </lang> compile it: gfortran main.c query.f90 -o main.x

Go

Possible—if you allow a small stretch of the task specification.

Cgo, Go's interface to C, allows calls from C to Go, but only if it gets to start Go first. That is, it doesn't work with a program started with C startup code and C main(), but only with a program started with Go startup code and Go main().

Thus, I changed the specified C code to begin as follows, <lang c>#include <stdio.h>

  1. include "_cgo_export.h"

void Run() {

  char     Buffer [1024];
  size_t   Size = sizeof (Buffer);
  if (0 == Query (Buffer, &Size))
  ...</lang>

The biggest change is that I renamed main, since it is no longer a C main function. Another small change is that the extern declaration is replaced by an include. The included file is generated by cgo and contains an equivalent extern declaration.

In the Go code, below, you see that all main does is call C.Run. The C code is then in the driver's seat. <lang go>package main

// #include <stdlib.h> // extern void Run(); import "C" import "unsafe"

func main() {

   C.Run()

}

const msg = "Here am I"

//export Query func Query(cbuf *C.char, csiz *C.size_t) C.int {

   if int(*csiz) <= len(msg) {
       return 0
   }
   pbuf := uintptr(unsafe.Pointer(cbuf))
   for i := 0; i < len(msg); i++ {
       *((*byte)(unsafe.Pointer(pbuf))) = msg[i]
       pbuf++
   }
   *((*byte)(unsafe.Pointer(pbuf))) = 0
   *csiz = C.size_t(len(msg) + 1)
   return 1

}</lang> Output:

Here am I

Alternative Method

As of Go 1.5, this is now possible without modifying the C code thanks to the addition of buildmodes. Buildmodes allow Go code to be compiled to standard C libraries (both dynamic and static).

The Go code for this task is as follows: <lang go> // This buildmode requires the package to be main package main

// Import C so we can export the function to C and use C types

//#include <stdlib.h> // for size_t import "C"

// Import reflect and unsafe so we can wrap the C array in a Go slice import "reflect" import "unsafe"

// This buildmode also requires a main function, but it is never actually called func main() {}

// The message to copy into the buffer const msg = "Here am I"

// Here we declare the Query function using C types and export it to C

//export Query func Query(buffer *C.char, length *C.size_t) C.int {

       // Check there is enough space in the buffer
       if int(*length) < len(msg) {
               return 0
       }
       // Wrap the buffer in a slice to make it easier to copy into
       sliceHeader := reflect.SliceHeader {
               Data: uintptr(unsafe.Pointer(buffer)),
               Len: len(msg),
               Cap: len(msg),
       }
       bufferSlice := *(*[]byte)(unsafe.Pointer(&sliceHeader))
       // Iterate through the message and copy it to the buffer, byte by byte
       for i:=0;i<len(msg);i++ {
               bufferSlice[i] = msg[i]
       }
       // Set length to the amount of bytes we copied
       (*length) = C.size_t(len(msg))
       return 1

} </lang>

Assuming this is saved to query.go (and that the C code is saved as main.c) it can be compiled with:

$ go build -buildmode=c-shared query.go
$ gcc main.c -L. -lquery -o main

This creates a library file, a header file for the library (not used) and an executable dynamically linked to the library file.

The executable can be run with:

$ LD_LIBRARY_PATH=. ./main
Here am I

Haskell

I modified the C source to include Haskell-specific headers and to init the Haskell environment. I also changed "Query" to "query_hs" due to capitalization issues: <lang c>#ifdef __GLASGOW_HASKELL__

  1. include "Called_stub.h"

extern void __stginit_Called(void);

  1. endif
  2. include <stdio.h>
  3. include <HsFFI.h>

int main (int argc, char * argv []) {

   char     Buffer [1024];
   size_t   Size = sizeof (Buffer);
   hs_init(&argc, &argv);
  1. ifdef __GLASGOW_HASKELL__
   hs_add_root(__stginit_Called);
  1. endif
   if (0 == query_hs (Buffer, &Size))
       {
           printf ("failed to call Query\n");
       }
   else
       {
           char * Ptr = Buffer;
           while (Size-- > 0) putchar (*Ptr++);
           putchar ('\n');
       }
   hs_exit();
   return 0;

}</lang>

The Haskell code then is:

<lang haskell>{-# LANGUAGE ForeignFunctionInterface #-}

module Called where

import Foreign import Foreign.C.String (CString, withCStringLen) import Foreign.C.Types

-- place a string into the buffer pointed to by ptrBuff (with size -- pointed to by ptrSize). If successful, sets number of overwritten -- bytes in ptrSize and returns 1, otherwise, it does nothing and -- returns 0 query_hs :: CString -> Ptr CSize -> IO CInt query_hs ptrBuff ptrSize = withCStringLen "Here I am"

              (\(str, len) -> do
                  buffSize <- peek ptrSize
                  if sizeOf str > (fromIntegral buffSize)
                    then do
                      poke ptrSize 0
                      return 0
                    else do
                      poke ptrSize (fromIntegral len)
                      copyArray ptrBuff str len
                      return 1)

foreign export ccall query_hs :: CString -> Ptr CSize -> IO CInt</lang>

Compile the Haskell code with: <lang bash>ghc -c -O Called.hs</lang>

Then compile the C code together with the generated Haskell files (using GHC): <lang bash>ghc -optc-O calling.c Called.o Called_stub.o -o calling</lang>

Output:

Here I am

Haxe

PHP

<lang haxe>untyped __call__("functionName", args);</lang>

J

Install an input and an output routine to use the J engine externally. These pass character strings arguments. To complete the task, I made these two routines communicate using compilation-unit scope variables (static). I tested the program on a 64 bit Ubuntu linux 2011 Autumn release with j versions 602 and 701. Comment of asterisks marks the input and output routines.

The J verb evaluates to the string unless there is no space. File rc_embed.ijs <lang J> query=:3 :'0&#^:(y < #)Here am I' </lang>

main.c <lang c>

  1. include<stdio.h>
  2. include<stdlib.h>
  3. include<string.h>

int Query(char*,unsigned*);

int main(int argc,char*argv[]) {

 char Buffer[1024], *pc;
 unsigned Size = (unsigned)sizeof(Buffer);
 if (!Query(Buffer,&Size))
   fputs("Failed to call Query",stdout);
 else
   for (pc = Buffer; Size--; ++pc)
     putchar(*pc);
 putchar('\n');
 return EXIT_SUCCESS;

} </lang>

Query.c <lang c>

// J Front End Example // define _WIN32 for Windows, __MACH__ for MAC, J64 for 64-bit // JE is loaded from current working directory

//make jfex && LD_LIBRARY_PATH=/usr/local/j64-701/bin ./jfex

  1. ifdef _WIN32
  2. define _CRT_SECURE_NO_WARNINGS
  3. include <windows.h>
  4. include <direct.h>
  5. define GETPROCADDRESS(h,p) GetProcAddress(h,p)
  6. define JDLLNAME "\\j.dll"
  7. else
  8. define _stdcall
  9. include <dlfcn.h>
  10. define GETPROCADDRESS(h,p) dlsym(h,p)
  11. ifdef __MACH__
  12. define JDLLNAME "/libj.dylib"
  13. else
  14. define JDLLNAME "/libj.so"
  15. endif
  16. define _getcwd getcwd
  17. endif
  1. include<stdio.h>
  2. include<signal.h>
  3. include<stdlib.h>
  4. include<string.h>
  5. include"jfex.h"
  6. include"jlib.h"

static JDoType jdo; static JFreeType jfree; static JgaType jga; static JGetLocaleType jgetlocale;

static J jt; static void* hjdll;

static char **adadbreak; static void sigint(int k){**adadbreak+=1;signal(SIGINT,sigint);} static char input[1000];

// J calls for input (debug suspension and 1!:1[1) and we call for input char* _stdcall Jinput(J jt,char* prompt) {

 fputs(prompt,stdout);
 if(fgets(input, sizeof(input), stdin))
   {
     fputs("\n",stdout);
     **adadbreak+=1;
   }
 return input;

}

static char*buffer = NULL; /**************************************/ static unsigned length = 0; /**************************************/ static int Jouts = 0; /**************************************/

// J calls for output

  1. define LINEFEED 10 /**************************************/

void _stdcall Joutput(J jt,int type, char* s) /********************/ {

 size_t L;
 if(MTYOEXIT==type) exit((int)(I)s);
 L = strlen(s);
 L -= (L && (LINEFEED==s[L-1])); /* CRLF not handled. */
 if (L && (!Jouts)) {
   length = L;
   strncpy(buffer,s,L);
   Jouts = 1;
 }

}

int Query(char*Data,unsigned*Length) {

 void* callbacks[] = {Joutput,NULL,Jinput,0,(void*)SMCON};
 char pathdll[1000];
 _getcwd(pathdll,sizeof(pathdll));
 strcat(pathdll,JDLLNAME);
  1. ifdef _WIN32
 hjdll=LoadLibraryA(pathdll);
  1. else
 hjdll=dlopen(pathdll,RTLD_LAZY);
 if (NULL == hjdll)
   hjdll=dlopen(JDLLNAME+1,RTLD_LAZY); /* use LD_LIBRARY_PATH */
  1. endif
 if(NULL == hjdll)
   {
     fprintf(stderr,"Unix use: $ LD_LIBRARY_PATH=path/to/libj.so %s\n","programName");//*argv);
     fputs("Load library failed: ",stderr);
     fputs(pathdll,stderr);
     fputs("\n",stderr);
     return 0; // load library failed
   }
 jt=((JInitType)GETPROCADDRESS(hjdll,"JInit"))();
 if(!jt) return 0; // JE init failed
 ((JSMType)GETPROCADDRESS(hjdll,"JSM"))(jt,callbacks);
 jdo=(JDoType)GETPROCADDRESS(hjdll,"JDo");
 jfree=(JFreeType)GETPROCADDRESS(hjdll,"JFree");
 jga=(JgaType)GETPROCADDRESS(hjdll,"Jga");
 jgetlocale=(JGetLocaleType)GETPROCADDRESS(hjdll,"JGetLocale");
 adadbreak=(char**)jt; // first address in jt is address of breakdata
 signal(SIGINT,sigint);
 {
   char input[999];
   //memset(input,0,sizeof input);
   buffer = Data;
   sprintf(input,"query %u [ 0!:110<'rc_embed.ijs'\n",*Length); /***deceptive input routine, a hard coded string*********/
   jdo(jt,input);
   if (!Jouts)
     return 0;
   *Length = length;
 }
 jfree(jt);
 return 1;

} </lang>

makefile, adjust for your j installation. <lang make>

  1. jfe makefile info
  2. customize to create makefile suitable for your platform
  3. 32bit builds on 64bit systems require -m32 in CFLAGS and FLAGS
  4. Unix requires -ldl in FLAGS and Windows does not

CPPFLAGS= -I/usr/local/j64-602/system/examples/jfe CFLAGS= -O0 -g LOADLIBES= -ldl

main: main.o Query.o </lang>

Finally, build and execution. Again, adjust LD_LIBRARY_PATH to the directory of libj.so . <lang bash> $ make main && LD_LIBRARY_PATH=~/Downloads/jgplsrc/j/bin ./main Here am I $ </lang>

Lisaac

query.li <lang Lisaac>Section Header

+ name := QUERY; - external := `#define main _query_main`; - external := `#define query Query`;

Section External

- query(buffer : NATIVE_ARRAY[CHARACTER], size : NATIVE_ARRAY[INTEGER]) : INTEGER <- (

 + s : STRING_CONSTANT;
 + len, result : INTEGER;
 s := "Here am I";
 len := s.count;
 (len > size.item(0)).if {
   result := 0;
 } else {
   1.to len do { i : INTEGER;
     buffer.put (s @ i) to (i - 1);
   };
   size.put len to 0;
   result := 1;
 };
 result

);

Section Public

- main <- (

 + buffer : NATIVE_ARRAY[CHARACTER];
 + size : NATIVE_ARRAY[INTEGER];
 query(buffer, size); // need this to pull the query() method

);</lang> Makefile <lang lisaac>TARGET=test_query

all: $(TARGET)

$(TARGET): main.o query.o gcc -o $@ main.o query.o

.c.o: gcc -c $<

query.c: query.li -lisaac $<

clean: rm -f $(TARGET) *.o query.c</lang>

Nim

<lang nim>proc Query*(data: var array[1024, char], length: var cint): cint {.exportc.} =

 const text = "Here am I"
 if length < text.len:
   return 0
 for i in 0 .. <text.len:
   data[i] = text[i]
 length = text.len
 return 1</lang>

Compile the above with nim c --app:staticlib --no_main query.nim. <lang c>#include <stdio.h>

extern int Query (char * Data, size_t * Length);

int main (int argc, char * argv []) {

  char     Buffer [1024];
  size_t   Size = sizeof (Buffer);
  if (0 == Query (Buffer, &Size))
  {
     printf ("failed to call Query\n");
  }
  else
  {
     char * Ptr = Buffer;
     while (Size-- > 0) putchar (*Ptr++);
     putchar ('\n');
  }

}</lang> Compile the above with gcc -ldl -o main main.c libquery.nim.a, then execute the resulting main binary:

./main
Here am I

OCaml

<lang c>#include <stdio.h>

  1. include <string.h>
  2. include <caml/mlvalues.h>
  3. include <caml/callback.h>

extern int Query (char * Data, size_t * Length) {

  static value * closure_f = NULL;
  if (closure_f == NULL) {
      closure_f = caml_named_value("Query function cb");
  }
  value ret = caml_callback(*closure_f, Val_unit);
  *Length = Int_val(Field(ret, 1));
  strncpy(Data, String_val(Field(ret, 0)), *Length);
  return 1;

}

int main (int argc, char * argv []) {

  char     Buffer [1024];
  unsigned Size = 0;
  caml_main(argv);  /* added from the original main */
  if (0 == Query (Buffer, &Size))
  {
     printf ("failed to call Query\n");
  }
  else
  {
     char * Ptr = Buffer;
     printf("size: %d\n", Size);
     while (Size-- > 0) putchar (*Ptr++);
     putchar ('\n');
  }

}</lang>

<lang ocaml>let caml_query () =

 let s = "Here am I" in
 (s, String.length s)

let () =

 Callback.register "Query function cb" caml_query;
</lang>

compile with:

ocamlopt -output-obj caml_part.ml -o caml_part_obj.o
gcc -c main.c  -I"`ocamlc -where`"
gcc -o prog.opt  main.o  caml_part_obj.o \
      -L"`ocamlc -where`" \
      -lm -ldl -lasmrun

PARI/GP

This is a Linux solution. Message "Here I am" is encrypted with ROT13: "Urer V nz".

ROT13() is implemented as a PARI one-liner:<lang parigp>Strchr(Vecsmall(apply(k->if(k>96&&k<123,(k-84)%26+97,if(k>64&&k<91,(k-52)%26+65,k)),Vec(Vecsmall(s)))))</lang>

PARI's interface for Query()... query.c: <lang C>#include <pari/pari.h>

  1. define PARI_SECRET "s=\"Urer V nz\";Strchr(Vecsmall(apply(k->if(k>96&&k<123,(k-84)%26+97,if(k>64&&k<91,(k-52)%26+65,k)),Vec(Vecsmall(s)))))"

int Query(char *Data, size_t *Length) {

 int rc = 0;
 GEN result;
 pari_init(1000000, 2);
 result = geval(strtoGENstr(PARI_SECRET));     /* solve the secret */
 if (result) {
   strncpy(Data, GSTR(result), *Length);	/* return secret */
   rc = 1;
 }
 pari_close();
 return rc;

}</lang>

Compile interface to a library: gcc -O2 -Wall -fPIC -shared query.c -o libquery.so -lpari

Compile main() C code from above and link against this library: gcc -O2 -Wall main.c -o main -L. libquery.so

Start main(): LD_LIBRARY_PATH=. ./main

PARI solves the ROT13 encrypted message and returns result to caller.

Output:

Here I am

NB. It's also possible to compile both files together without building an interface: gcc -O2 -Wall main.c query.c -o main2 -lpari

./main2 yields same output as stated above.

Pascal

See Delphi

Phix

The following code declares a callback for the C code (which I'd expect to be in a .dll or .so) to invoke.
A 32-bit-only or a 64-bit-only version would of course be slightly shorter. <lang Phix>constant Here_am_I = "Here am I" function Query(atom pData, atom pLength) integer len = peekNS(pLength,machine_word(),0)

   if poke_string(pData,len,Here_am_I) then
       return 0
   end if
   pokeN(pLength,length(Here_am_I)+1,machine_word())
   return 1

end function constant Query_cb = call_back(routine_id("Query"))</lang>

PicoLisp

Calling a PicoLisp function from another program requires a running interpreter. There are several possibilities, like IPC via fifo's or sockets using the PLIO (PicoLisp-I/O) protocol, but the easiest is calling the interpreter in a pipe. This is relatively efficient, as the interpreter's startup time is quite short.

If there is a file "query.l" <lang PicoLisp>(let (Str "Here am I" Len (format (opt))) # Get length from command line

  (unless (>= (size Str) Len)              # Check buffer size
     (prinl Str) ) )                       # Return string if OK</lang>

then the C function 'Query' could be <lang C>int Query(char *Data, size_t *Length) {

  FILE *fp;
  char buf[64];
  sprintf(buf, "/usr/bin/picolisp query.l %d -bye", *Length);
  if (!(fp = popen(buf, "r")))
     return 0;
  fgets(Data, *Length, fp);
  *Length = strlen(Data);
  return pclose(fp) >= 0 && *Length != 0;

}</lang>

Python

Our embedded python function a) uses information from the main routine in c, and b) determines the information to populate the result returned to the main routine. This, I believe, fulfills the task requirement. The modifications and compilation are shown for Ubuntu linux Autumn 2011 version, with python3. It's easier to call a dynamic library from python using the ctypes module. Consider using PyRun_SimpleString to have main.c call python calling back to c. <lang python>

  1. store this in file rc_embed.py
  2. store this in file rc_embed.py

def query(buffer_length):

   message = b'Here am I'
   L = len(message)
   return message[0:L*(L <= buffer_length)]

</lang>

main.c <lang c>

  1. if 0

//I rewrote the driver according to good sense, my style, //and discussion --Kernigh 15:45, 12 February 2011 (UTC).

  1. endif
  1. include<stdio.h>
  2. include<stdlib.h>
  3. include<string.h>

extern int Query(char*,unsigned*);

int main(int argc,char*argv[]) {

 char Buffer[1024], *pc;
 unsigned Size = sizeof(Buffer);
 if (!Query(Buffer,&Size))
   fputs("Failed to call Query",stdout);
 else
   for (pc = Buffer; Size--; ++pc)
     putchar(*pc);
 putchar('\n');
 return EXIT_SUCCESS;

} </lang>

In Query.c I don't promise to have tested every case with missing module, missing function, or to have used Py_DECREF correctly. <lang c>

  1. include<stdio.h>
  2. include<stdlib.h>
  3. include<string.h>
  4. include<Python.h>

int Query(char*Data,unsigned*Length) {

 char *module = "rc_embed", *function = "query";
 PyObject *pName, *pModule, *pFunc, *pResult, *pArgs, *pLength;
 long result = 0;
 if (!Py_IsInitialized())
   Py_Initialize();
 pName = PyUnicode_FromString(module);
 pModule = PyImport_Import(pName);
 Py_DECREF(pName);
 if (NULL == pModule) {
   PyErr_Print();
   fprintf(stderr,"Failed to load \"%s\"\n",module);
   return 0;
 }
 pFunc = PyObject_GetAttrString(pModule,function);
 if ((NULL == pFunc) || (!PyCallable_Check(pFunc))) {
   if (PyErr_Occurred())
     PyErr_Print();
   fprintf(stderr,"Cannot find function \"%s\"\n",function);
   if (NULL != pFunc)
     Py_DECREF(pFunc);
   Py_DECREF(pModule);
   return 0;
 }	
 pArgs = PyTuple_New(1);
 pLength = PyLong_FromUnsignedLong((unsigned long)(*Length));
 if (NULL == pLength) {
   Py_DECREF(pArgs);
   Py_DECREF(pFunc);
   Py_DECREF(pModule);
   return 0;
 }
 PyTuple_SetItem(pArgs,0,pLength);
 pResult = PyObject_CallObject(pFunc, pArgs);
 if (NULL == pResult)
   result = 0;
 else if (!PyBytes_Check(pResult)) {
   result = 0;
   Py_DECREF(pResult);
 } else {
   if (! PyBytes_Size(pResult))
     result = 0;
   else {
     *Length = (unsigned)PyBytes_Size(pResult);
     strncpy(Data,PyBytes_AsString(pResult),*Length);
     Py_DECREF(pResult);
     result = 1;
   }
 }
 Py_DECREF(pArgs);
 Py_DECREF(pFunc);
 Py_DECREF(pModule);
 Py_Finalize();
 return result;

} </lang>

Compilation, linkage, execution. Note the python tools used to extract the correct flags. <lang bash> $ make main.o cc -c -o main.o main.c $ D=$( dirname $( which python3 ) ) $ gcc $( $D/python3.2-config --cflags ) -c Query.c In file included from /usr/include/python3.2mu/Python.h:8:0,

                from Q.c:18:

/usr/include/python3.2mu/pyconfig.h:1173:0: warning: "_POSIX_C_SOURCE" redefined [enabled by default] /usr/include/features.h:214:0: note: this is the location of the previous definition $ gcc -o main main.o Query.o $( $D/python3.2-config --ldflags ) $ ./main Here am I $ </lang>

Racket

Since this problem is presented as the inverse to Call foreign language function, I've focused on just demonstrating a callback from C into Racket, instead of showing how to embed the whole Racket runtime into C.

Starting with the given C code, modify it so that Query is a variable instead of an external:

<lang C> typedef int strfun (char * Data, size_t * Length); strfun *Query = NULL; </lang>

The rest of the C code is left as-is. Compile it into a dynamic library, then run the following Racket code:

<lang racket>

  1. lang racket

(require ffi/unsafe)

(define xlib (ffi-lib "./x.so"))

(set-ffi-obj! "Query" xlib (_fun _pointer _pointer -> _bool)

 (λ(bs len)
   (define out #"Here I am")
   (let ([bs (make-sized-byte-string bs (ptr-ref len _int))])
     (and ((bytes-length out) . <= . (bytes-length bs))
          (begin (bytes-copy! bs 0 out)
                 (ptr-set! len _int (bytes-length out))
                 #t)))))

((get-ffi-obj "main" xlib (_fun _int (_list i _bytes) -> _void))

0 '())

</lang>

Note that this code is intentionally in a simple low-level form, for example, it sets the pointer directly instead of using a C function.

The output is the expected “Here I am” line.

Tcl

The way you would tackle this problem depends on whether you are working with ‘In’ or ‘Out’ parameters. (It is normal model ‘inout’ parameters as Tcl variables; omitted for brevity.)

‘In’ Parameters

To connect a function to Tcl that passes an arbitrary C string as input, you'd use a short C thunk, like this: <lang c>int Query (char * Data, size_t * Length) {

   Tcl_Obj *arguments[2];
   int code;
   arguments[0] = Tcl_NewStringObj("Query", -1); /* -1 for "use up to zero byte" */
   arguments[1] = Tcl_NewStringObj(Data, Length);
   Tcl_IncrRefCount(arguments[0]);
   Tcl_IncrRefCount(arguments[1]);
   if (Tcl_EvalObjv(interp, 2, arguments, 0) != TCL_OK) {
       /* Was an error or other exception; report here... */
       Tcl_DecrRefCount(arguments[0]);
       Tcl_DecrRefCount(arguments[1]);
       return 0;
   }
   Tcl_DecrRefCount(arguments[0]);
   Tcl_DecrRefCount(arguments[1]);
   if (Tcl_GetObjResult(NULL, Tcl_GetObjResult(interp), &code) != TCL_OK) {
       /* Not an integer result */
       return 0;
   }
   return code;

}</lang> Which would lead to a Query implementation like this: <lang tcl>proc Query data {

   puts "Query was $data"
   return 1;

}</lang>

‘Out’ Parameters

However, in the specific case of writing to a user-specified buffer (an “out” parameter) the thunk code would instead manage copying the result from the interpreter back to the buffer: <lang tcl>int Query (char * Data, size_t * Length) {

   const char *str;
   int len;
   if (Tcl_Eval(interp, "Query") != TCL_OK) {
       return 0;
   }
   str = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len);
   if (len+1 > Length) {
       return 0;
   }
   memcpy(Data, str, len+1);
   return 1;

}</lang> And the implementation of Query would be just: <lang tcl>proc Query {} {

   return "Here am I"

}</lang> (Since this is working with a literal, this would actually be efficient and just result in references being passed.)

Connecting up the pieces

You would also need a short piece of code in main() to initialize the Tcl library and create an interpreter instance, and you would need to build and link against libtcl. <lang c>#include <tcl.h> Tcl_Interp *interp;

int main(int argc, char **argv) {

   Tcl_FindExecutable(argv[0]); /* Initializes library */
   interp = Tcl_CreateInterp(); /* Make an interpreter */
   /* Rest of contents of main() from task header... */

}</lang>

TXR

Using character array

This is really two tasks: how to accept foreign callbacks, and how to link code to a C program which controls the main startup function.

The TXR run-time is not available as a library that can be linked to a C program. Instead, we can put the C driver into a small library and call out to it from TXR, then accept its callback. Here is that library:

<lang c>#include <stdio.h>

int query(int (*callback)(char *, size_t *)) {

 char buffer[1024];
 size_t size = sizeof buffer;
 if (callback(buffer, &size) == 0) {
   puts("query: callback failed");
 } else {
   char *ptr = buffer;
   while (size-- > 0)
     putchar (*ptr++);
   putchar('\n');
 }

}</lang>

Here are the build steps to produce a `query.so` object from it on GNU/Linux:

<lang shell>gcc -g -fPIC query.c -c gcc -g --shared query.c -o query.c</lang>

Now an interactive TXR session.

Callbacks are modeled as "FFI closures". The macro deffi-cb defines a function which itself isn't a callback, but is rather a combinator which converts a Lisp function into a FFI callback.

<lang txrlisp>(with-dyn-lib "./query.so"

 (deffi query "query" void (closure)))

(deffi-cb query-cb int ((ptr (array 1024 char)) (ptr (array 1 size-t))))

(query (query-cb (lambda (data sizeptr)

                  (symacrolet ((size [sizeptr 0]))                             
                    (let* ((s "Here am I")                                     
                           (l (length s)))                                     
                      (cond                                                    
                        ((> l size) 0)                                         
                        (t (set [data :..:] s)                                 
                           (set size l))))))))</lang>
Output:
Here am I

Note that the obvious way of passing a size_t value by pointer, namely (ptr size-t) doesn't work. While the callback will receive the size (FFI will decode the pointer type's semantics and get the size value), updating the size will not propagate back to the caller, because it becomes, effectively, a by-value parameter. A (ptr size-t) object has to be embedded in an aggregate that is passed by reference, in order to have two-way semantics. Here we use the trick of treating the size_t * as an array of 1, which it de facto is. In the callback, we establish local symbol macro which lets us just refer to [sizeptr 0] it as size.

Note also how the data is prepared. As a special case, FFI creates a correspondence between the char array and a character string. The callback must mutate the character string to the desired value; FFI will then propagate the mutation to the original array. If the callback mistakenly performed (set data s), it wouldn't work, because the original string object is untouched. Only the lexical data variable is replaced with a pointer s. The expression (set [data :..:] s) replaces a subrange of data with s, where the subrange is all of data.

Finally, note that TXR Lisp strings are Unicode, stored as arrays of wide characters (C type wchar_t). FFI is doing automatic conversion between that representation and UTF-8. That's a specialized behavior of the (array ... char)> type. If UTF-8 encoding is undesirable, then the bchar type can be used (byte char). Then there is a one to one correspondence between the Unicode characters and array elements. However, out-of-range Unicode characters (values above U+007F) trigger an exception.

Using carray

Our above approach has a problem: it uses FFI in a way that relies on knowing the size of the C object, which is incorrect. The C buffer could be of any size; the only indicator we can trust is the run-time value we are given.

To accurately deal with this kind of situation accurately, the lower level carray FFI type can be used:

<lang txrlisp>;; callback signature is altered to take "carray of char": (with-dyn-lib "./query.so"

 (deffi query "query" void (closure)))

(deffi-cb query-cb int ((carray char) (ptr (array 1 size-t))))

(query (query-cb (lambda (buf sizeptr)

                  (symacrolet ((size [sizeptr 0]))
                    (carray-set-length buf size)
                    (let* ((s "Here am I")
                           (l (length s)))
                      (cond
                        ((> l size) 0)
                        (t (each ((i (range* 0 l)))
                             (carray-refset buf i [s i]))
                           (set size l))))))))</lang>

If the callback throws an exception or performs any other non-local return, it will return a default return value of all zero bits in the given return type. This value can be specified, but the zero default suits our particular situation:

$ txr
This is the TXR Lisp interactive listener of TXR 177.
Use the :quit command or type Ctrl-D on empty line to exit.
1> (with-dyn-lib "./query.so" (deffi query "query" void (closure)))
#:lib-0177
2> (deffi-cb query-cb int ((ptr (array 1024 char)) (ptr size-t)))
query-cb
3> (query (query-cb (lambda (x y) (error "oops"))))
query: callback failed
** oops
** during evaluation at expr-3:1 of form (error "oops")
4>

Here we can see that when the callback throws the error exception, the C code prints query: callback failed, due to receiving the default abort return value of zero. Then, the exception continues up to the interactive prompt.

Using cptr and memcpy

A more succinct approach is possible if we avail ourselves of the memcpy function via FFI. We can receive the data as an opaque foreign pointer represented by the cptr type. We can set up memcpy so that its destination argument and return value is a cptr, but the source argument is a string:

<lang txrlisp>(with-dyn-lib "./query.so"

 (deffi query "query" void (closure)))                                         
                                                                               

(with-dyn-lib nil

 (deffi memcpy "memcpy" cptr (cptr str size-t)))                               
                                                                               

(deffi-cb query-cb int (cptr (ptr (array 1 size-t))))

(query (query-cb (lambda (buf sizeptr)  ; int lambda(void *buf, size_t *sizeptr)

                  (symacrolet ((size [sizeptr 0])) ;  { #define size sizeptr[0]
                    (let* ((s "Here am I")         ;    char *s = "Here am I";
                           (l (length s)))         ;    size_t l = strlen(s);
                      (cond                        ;    if (length > size)
                        ((> l size) 0)             ;    { return 0; } else
                        (t (memcpy buf s l)        ;    { memcpy(buf, s, l);
                           (set size l))))))))     ;      return size = l; } }</lang>

Here, the use of the str type in the memcpy interface means that FFI automatically produces a UTF-8 encoding of the string in a temporary buffer. The pointer to that temporary buffer is what is passed into memcpy. The temporary buffer is released after memcpy returns.

To reveal the similarity between the Lisp logic and how a C function might be written, the corresponding C code is shown. However, that C code's semantics is, of course, devoid of any hidden UTF-8 conversion.

zkl

To make this as simple as possible, the [zkl] query program sets a variable and main.c runs query.zkl and extracts the variable. A more realistic scenario (which several of the extension libraries utilize) is to compile the zkl code, wad it into C code (a byte stream of the compiled code) and link that with main. Not hard but messy (the source of a suitable extension gives you something to copy). Also, this solution uses the shared library version of zkl (you could use the all in one version but you would go about it in a [slightly] different way).

Modified main.c: <lang c>// query.c // export zklRoot=/home/ZKL // clang query.c -I $zklRoot/VM -L $zklRoot/Lib -lzkl -pthread -lncurses -o query // LD_LIBRARY_PATH=$zklRoot/Lib ./query

  1. include <stdio.h>
  2. include <string.h>
  1. include "zklObject.h"
  2. include "zklImports.h"
  3. include "zklClass.h"
  4. include "zklFcn.h"
  5. include "zklString.h"

int query(char *buf, size_t *sz) {

  Instance *r;
  pVM       vm;
  MLIST(mlist,10);
  // Bad practice: not protecting things from the garbage collector
  // build the call parameters: ("query.zkl",False,False,True)
  mlistBuild(mlist,stringCreate("query.zkl",I_OWNED,NoVM),
             BoolFalse,BoolFalse,BoolTrue,ZNIL);
  // Import is in the Vault, a store of useful stuff
  // We want to call TheVault.Import.import("query.zkl",False,False,True)
  //    which will load/compile/run query.zkl
  r = fcnRunith("Import","import",(Instance *)mlist,NoVM);
  // query.zkl is a class with a var that has the query result
  r = classFindVar(r,"query",0,NoVM);  // -->the var contents
  strcpy(buf,stringText(r));     // decode the string into a char *
  *sz = strlen(buf);   // screw overflow checking
  return 1;

}

int main(int argc, char* argv[]) {

  char   buf[100];
  size_t sz = sizeof(buf);
  zklConstruct(argc,argv);	// initialize the zkl shared library
  query(buf,&sz);
  printf("Query() --> \"%s\"\n",buf);
  return 0;

}</lang> Our query program: <lang zkl>// query.zkl var query="Here am I";</lang> On Linux:

Output:
$ clang query.c -I $zklRoot/VM -L $zklRoot/Lib -lzkl -pthread -lncurses -o query
$ LD_LIBRARY_PATH=$zklRoot/Lib ./query
Query() --> "Here am I"