Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tcl C pass by reference

23 views
Skip to first unread message

derek....@tesco.net

unread,
Aug 4, 2005, 8:47:26 AM8/4/05
to
Hi all

I have recently been looking at writing a C based extension.
This is mainly to wrap some C library procedures that manipulate test
equipment.
At this stage, as I'm relatively knew, I'm keeping it seperate to the
original C source. Effectively I'm writing a simple set of Tcl C
wrappers.

I know of SWIG and CRITCL but want to use this as alearning experience
for more detailed applications in the future.

There are two approaches to carrying out this wrapping process, one is
to represent the C interface at the scripting level. the other is to
think more from the scripting side an how the results from the commands
are best reported to the scripts.

At the moment I under stand how to pas usefull information back up to
the script as a string but there are some C functions that pass up
multiple results, firmware major minor for instance.

My attempts to provide a pass by refernce were coded below.
This raises a lot of questions, which I'm sure would be explained as I
become more familiar with the TCL C Library. I thought I'd present this
code here so I could benefit from the more experienced extension
writers.

Some of my questions are:

What happens to the pased in objects?
I simply used these to carry a name of a variable to create

Do I need to manipulate the refernce count of the duplicated
objects?

Will Tcl_Objs be dispossed of for me?

Am I causing memory leaks?

Is there a preferd method of doing this?

Pass by reference attempt 1.


/* Trivial example os C function */
int fred(int *major,int *minor)
{
*major=1;
*minor=2;
return;
}


/* Tcl Obj command that provides a tcl script interface to C function
*/

int fredTcl(ClientData clientdata, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[])
{
Tcl_Obj *obj1 , *obj2;
int maj, min;
char *name1, *name2;

if (objc < 3) {
Tcl_WrongNumArgs(interp, 1, objv, "major minor");
return TCL_ERROR;
}

obj1=objv[1];
obj2=objv[2];

if (Tcl_IsShared(obj1)) {
obj1=Tcl_DuplicateObj(obj1);
obj2=Tcl_DuplicateObj(obj2);
}

/* lets call a C func */
fred(&maj,&min);

/* lets create a couple of globals to carry the result */
name1=Tcl_GetString(obj1);
name2=Tcl_GetString(obj2);

Tcl_SetVar2Ex(interp,name1,NULL,obj1,0);
Tcl_SetVar2Ex(interp,name2,NULL,obj2,0);

/*Now lets set the values */
Tcl_SetIntObj(obj1,maj);
Tcl_SetIntObj(obj2,min);

return TCL_OK;
}

sp...@controlq.com

unread,
Aug 4, 2005, 10:49:46 AM8/4/05
to
On Thu, 4 Aug 2005 derek....@tesco.net wrote:

> At the moment I under stand how to pas usefull information back up to
> the script as a string but there are some C functions that pass up
> multiple results, firmware major minor for instance.
>

Why not use the array set property, and have your function return a simple
string which can be eval(uated) to set an array ... for instance your
C function might return a string:

"array set myreturn {
major 1
minor 2
}"

and the script might be similar to:

set retval [ myfunction $a $b $c ]
eval $retval
puts $myreturn(major)
1
puts $myreturn(minor)
2

Of course, a simple convention for naming the array (myreturn) in this case
would be part of the protocol contract between the C and TCL code, but it
would simplify your C code immensely, at the cost of fixing the return array
name, but there are simple workarounds for that too.

Hope this helps.

Cheers,
Rob.

bs

unread,
Aug 4, 2005, 11:33:49 AM8/4/05
to
well, in that case, I would return a list....either a list of values,
or a key value list. So, in your 'C' code, append to a list, and then
Tcl_SetObjResult that list. In your Tcl code, you can either do:

set myvalueslist [myfunction $a $b $c]
## then use [lindex] or [lassign] to get the values you want

OR

array set myvaluesarr [myfunction $a $b $c]

Helmut Giese

unread,
Aug 4, 2005, 1:05:13 PM8/4/05
to
Hi Derek,
You work too hard <g> - and you still think too much in terms of C
coding. Look, in Tcl it is absolutely easy to return multiple values:
Just return a list.

If I understood your requirements correctly I would do it like this:

int fred(int *major,int *minor)
{
*major=1;
*minor=2;

return; // what did you intend to return ????
}

/* Tcl Obj command that provides a tcl script interface to C function
*/
int fredTcl(ClientData clientdata, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[])
{

int major, minor;

// prepare a list
Tcl_Obj *lstPtr = Tcl_NewListObj( 0, NULL);

// get the result from 'fred'
fred( &major, &minor);

// stuff results into a Tcl list
Tcl_ListObjAppendElement( interp, lstPtr, Tcl_NewIntObj(major));
Tcl_ListObjAppendElement( interp, lstPtr, Tcl_NewIntObj(minor));

// make this list the result of the command
Tcl_SetObjResult(interp, lstPtr);

// everything is all right
return TCL_OK;
}

Now from Tcl you just do
foreach {major minor} [fred] {break}
and your (Tcl) variables major and minor will hold fred's result.

This is sort of a "generic" use of Tcl_NewListObj. In your case you
can shorten the code still a bit more by assigning in Tcl_NewListObj
the first list value (left as an exercise for the reader <g>).
HTH
Helmut Giese

derek....@tesco.net

unread,
Aug 4, 2005, 6:29:55 PM8/4/05
to
Hi Rob

An interesting Idea.
At the moment I have no influence on the C library and I am working in
skunk mode for this little project. As most of the usefull S/W is in
most places.

When the code and scripts get to a point of usefullness I'll concider
this.

Do you have any coments on the approach I had apart from it being very
C orientated..

Thanks in Advance


Derek

Message has been deleted

sp...@controlq.com

unread,
Aug 5, 2005, 12:58:25 PM8/5/05
to
On Thu, 4 Aug 2005 derek....@tesco.net wrote:

> Hi Rob
>
> An interesting Idea. At the moment I have no influence on the C library
> and I am working in skunk mode for this little project. As most of the
> usefull S/W is in most places.

The approach you use could be made to work just fine, but you are still
left with what to return to Tcl and how, particularly in the case of
multi-valued returns. The array remains the better solution, and as Tcl
has associative arrays, they are somewhat self-describing. Whether you
return the array as a string, or as an actual tcl object array already
constructed is up to you. The ideal solution would minimize the amount of
effort to interface the two code bases (C lib and Tcl Script), and be
generally applicable for re-use.

The approach I suggested is simpler and could be implemented via wrapper
routines, and so the C library could be arbitrary. The wrapper functions
would simply parse the args passed in, and process the return value(s),
and only the wrappers need be visible to Tcl.

Might help to have some C library functions to handle arg processing, and
construction of the return strings as well.

Helmut Giese

unread,
Aug 5, 2005, 3:20:25 PM8/5/05
to
On 4 Aug 2005 15:37:33 -0700, derek....@tesco.net wrote:
Hi Derek,
from your original post it was not clear (to me) that there were
several persons with different programming backgrounds involved. My
intent was to provide what I considered the most effective solution
for calling 'fred'.
>I was heading in that direction, although at the moment I'm sticking to
>returning strings as I know Tcl scripts are good with them.
But in Tcl "everything is a string" - so I would just return (in this
case) the integers from 'fred' and if later on you need them as
strings, well, Tcl will happily convert them for you.

>A lot of the test scripts are written by the S/W developers, I know its
>bad practice but.., and they tend to think in a C way, thats why I was
>trying to make it easier for them to use Tcl.
The way I see it, it is a /limitation/ of (languages like) C that they
can only return one value (let's ignore the possibility of returning
structs by value - this is no viable alternative), so we are forced to
supply lots of pointers to parameters where a function can store its
results.
Tcl does not have this limitation, and it must be a very specific set
of circumstances where it makes sense to introduce those to Tcl.

> I'm not sure many of them
>know enough about Tcl to have used the foreach { var1 var2 } alist
>{break} trick
Well, in a way my example contained 2 steps - which can (or maybe
should for "educatonal" purposes) well be separated:
1) A Tcl function can easily return as many values as you want via a
list. So you have
set resLst [fredTcl]
2) If you want to access the different elements the classical way is
set major [lindex $resLst 0]
set minor [lindex $resLst 1]

Only a couple days later you tell them: Oh, look, there is a nice
idiom to retrieve several elements of a list all at once:
foreach {major minor} $resLst {break}
or even
foreach {major minor} [fredTcl] {break}

>, although it seems quite straight forward to me. Its not
>a paradigm you see in C as a return from a func.
That's one of the reasons Tcl is superior :)

>Would there be any benefits of doing the equivalent of upvar at this
>level.
It would surely complicate matters, so I wouldn't even consider it,
but if you are in circumstances where it makes sense to do complicated
things in order to make Tcl appear more like C (shudder) - so be it.
Best regards
Helmut Giese

0 new messages