Use another language to call a function

From Rosetta Code
Jump to: navigation, search
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:

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

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

I rewrote the driver as

#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
#endif
 
#include <stdio.h>
#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;
}
 

With solution

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

And finally, excitement!

$ gcc main.c query.o -o main && ./main
Here am I
$
 

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

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

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:

#include <stdio.h>
#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;
}

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

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

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

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

#include <stdio.h>
#include "_cgo_export.h"
 
void Run()
{
char Buffer [1024];
size_t Size = sizeof (Buffer);
 
if (0 == Query (Buffer, &Size))
...

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.

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
}

Output:

Here am I

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

#ifdef __GLASGOW_HASKELL__
#include "Called_stub.h"
extern void __stginit_Called(void);
#endif
#include <stdio.h>
#include <HsFFI.h>
 
int main (int argc, char * argv [])
{
char Buffer [1024];
size_t Size = sizeof (Buffer);
 
hs_init(&argc, &argv);
#ifdef __GLASGOW_HASKELL__
hs_add_root(__stginit_Called);
#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;
}

The Haskell code then is:

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

Compile the Haskell code with:

ghc -c -O Called.hs

Then compile the C code together with the generated Haskell files (using GHC):

ghc -optc-O calling.c Called.o Called_stub.o -o calling

Output:

Here I am

[edit] Haxe

[edit] PHP

untyped __call__("functionName", args);

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

 
query=:3 :'0&#^:(y < #)''Here am I'''
 

main.c

 
#include<stdio.h>
#include<stdlib.h>
#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;
}
 

Query.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
 
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <direct.h>
#define GETPROCADDRESS(h,p) GetProcAddress(h,p)
#define JDLLNAME "\\j.dll"
#else
#define _stdcall
#include <dlfcn.h>
#define GETPROCADDRESS(h,p) dlsym(h,p)
#ifdef __MACH__
#define JDLLNAME "/libj.dylib"
#else
#define JDLLNAME "/libj.so"
#endif
#define _getcwd getcwd
#endif
 
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include"jfex.h"
#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
#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);
#ifdef _WIN32
hjdll=LoadLibraryA(pathdll);
#else
hjdll=dlopen(pathdll,RTLD_LAZY);
if (NULL == hjdll)
hjdll=dlopen(JDLLNAME+1,RTLD_LAZY); /* use LD_LIBRARY_PATH */
#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;
}
 

makefile, adjust for your j installation.

 
# jfe makefile info
# customize to create makefile suitable for your platform
# 32bit builds on 64bit systems require -m32 in CFLAGS and FLAGS
# 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
 

Finally, build and execution. Again, adjust LD_LIBRARY_PATH to the directory of libj.so .

 
$ make main && LD_LIBRARY_PATH=~/Downloads/jgplsrc/j/bin ./main
Here am I
$
 

[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] Nimrod

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

Compile the above with nimrod c --app:staticlib --no_main query.nim.

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

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

./main
Here am I

[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] Pascal

See Delphi

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

 
# store this in file rc_embed.py
# 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)]
 

main.c

 
#if 0
//I rewrote the driver according to good sense, my style,
//and discussion --Kernigh 15:45, 12 February 2011 (UTC).
#endif
 
#include<stdio.h>
#include<stdlib.h>
#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;
}
 

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

 
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#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;
}
 

Compilation, linkage, execution. Note the python tools used to extract the correct flags.

 
$ 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
$
 

[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
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox