Call a function from a foreign language

From Rosetta Code

Jump to: navigation, search
Task
Call a function from a foreign language
You are encouraged to solve this task according to the task description, using any language you may know.

This task is inverse to the task Call foreign language function. Consider the following C program:

 
#include <stdio.h>
 
extern int Query (char * Data, size_t * Length);
 
int main (int argc, char * argv [])
{
char Buffer [1024];
unsigned 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');
}
}
 

Write an implementation of Query in your language and make main calling it. The function Query takes the buffer a places the string Here am I into it. The buffer size in bytes is specified by 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.

Contents

[edit] Ada

The interface package Exported specification:

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;

The package implementation:

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;

With GNAT it can be built as follows:

gcc -c main.c
gnatmake -c exported.adb
gnatbind -n exported.ali
gnatlink exported.ali main.o -o main

Sample output:

Here am I

[edit] 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:
; 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.
}

[edit] C

 
#include <stdio.h>
 
extern int Query (char * Data, size_t * Length);
 
int main (int argc, char * argv [])
{
char Buffer [1024];
unsigned 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');
}
}
 

[edit] HaXe

[edit] PHP

untyped __call__("functionName", args);

[edit] Lisaac

query.li

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
);

Makefile

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

[edit] OCaml

#include <stdio.h>
#include <string.h>
#include <caml/mlvalues.h>
#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');
}
}
let caml_query () =
let s = "Here am I" in
(s, String.length s)
;;
 
let () =
Callback.register "Query function cb" caml_query;
;;

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

[edit] 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"

(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

then the C function 'Query' could be

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;
}

[edit] 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.)

[edit] ‘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:

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;
}

Which would lead to a Query implementation like this:

proc Query data {
puts "Query was $data"
return 1;
}

[edit] ‘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:

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;
}

And the implementation of Query would be just:

proc Query {} {
return "Here am I"
}

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

[edit] 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.

#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... */
}
Personal tools
Support