Use another language to call a function: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added HaXe language)
m (Fixed lang tags.)
Line 27: Line 27:
=={{header|Ada}}==
=={{header|Ada}}==
The interface package Exported specification:
The interface package Exported specification:
<lang Ada>with Interfaces.C; use Interfaces.C;
<lang Ada>
with Interfaces.C; use Interfaces.C;
with Interfaces.C.Strings; use Interfaces.C.Strings;
with Interfaces.C.Strings; use Interfaces.C.Strings;


Line 35: Line 34:
return int;
return int;
pragma Export (C, Query, "Query");
pragma Export (C, Query, "Query");
end Exported;
end Exported;</lang>
</lang>
The package implementation:
The package implementation:
<lang Ada>
<lang Ada>package body Exported is
package body Exported is
function Query (Data : chars_ptr; Size : access size_t)
function Query (Data : chars_ptr; Size : access size_t)
return int is
return int is
Line 52: Line 49:
end if;
end if;
end Query;
end Query;
end Exported;
end Exported;</lang>
</lang>
With [[GNAT]] it can be built as follows:
With [[GNAT]] it can be built as follows:
<lang sh>
<lang ada>gcc -c main.c
gcc -c main.c
gnatmake -c exported.adb
gnatmake -c exported.adb
gnatbind -n exported.ali
gnatbind -n exported.ali
gnatlink exported.ali main.o -o main
gnatlink exported.ali main.o -o main</lang>
</lang>
Sample output:
Sample output:
<pre>
<pre>
Line 68: Line 62:
=={{header|HaXe}}==
=={{header|HaXe}}==
=== PHP ===
=== PHP ===
<pre>untyped __call__("functionName", args);</pre>
<lang haxe>untyped __call__("functionName", args);</lang>


=={{header|Lisaac}}==
=={{header|Lisaac}}==
query.li
query.li
<lang Lisaac>
<lang Lisaac>Section Header
Section Header


+ name := QUERY;
+ name := QUERY;
Line 104: Line 97:
+ size : NATIVE_ARRAY[INTEGER];
+ size : NATIVE_ARRAY[INTEGER];
query(buffer, size); // need this to pull the query() method
query(buffer, size); // need this to pull the query() method
);
);</lang>
</lang>
Makefile
Makefile
<lang make>
<lang lisaac>TARGET=test_query
TARGET=test_query


all: $(TARGET)
all: $(TARGET)
Line 122: Line 113:


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


=={{header|OCaml}}==
=={{header|OCaml}}==
<lang C>#include <stdio.h>
<lang ocaml>#include <stdio.h>
#include <string.h>
#include <string.h>
#include <caml/mlvalues.h>
#include <caml/mlvalues.h>
Line 172: Line 162:
;;</lang>
;;</lang>


<lang sh>ocamlopt -output-obj caml_part.ml -o caml_part_obj.o
<lang ocaml>ocamlopt -output-obj caml_part.ml -o caml_part_obj.o
gcc -c main.c -I"`ocamlc -where`"
gcc -c main.c -I"`ocamlc -where`"
gcc -o prog.opt main.o caml_part_obj.o \
gcc -o prog.opt main.o caml_part_obj.o \
Line 182: Line 172:
===‘In’ Parameters===
===‘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:
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) {
<lang tcl>int Query (char * Data, size_t * Length) {
Tcl_Obj *arguments[2];
Tcl_Obj *arguments[2];
int code;
int code;
Line 211: Line 201:
===‘Out’ Parameters===
===‘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:
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 c>int Query (char * Data, size_t * Length) {
<lang tcl>int Query (char * Data, size_t * Length) {
const char *str;
const char *str;
int len;
int len;
Line 232: Line 222:
===Connecting up the pieces===
===Connecting up the pieces===
You would also need a short piece of code in <code>main()</code> to initialize the Tcl library and create an interpreter instance, and you would need to build and link against [[libtcl]].
You would also need a short piece of code in <code>main()</code> 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>
<lang tcl>#include <tcl.h>
Tcl_Interp *interp;
Tcl_Interp *interp;



Revision as of 16:11, 19 November 2009

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 is inverse to the task Call foreign language function. Consider the following C program: <lang C>

  1. 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');
  }

} </lang> 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.

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

HaXe

PHP

<lang haxe>untyped __call__("functionName", args);</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>

OCaml

<lang ocaml>#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>

<lang ocaml>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</lang>

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 tcl>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 tcl>#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>