From: Sean Woods on
Well Tclers, I have an interesting problem, and feel free to tell me
"Hypnotoad, that's not what NRE was designed for!"

To speed up my Tcl code, I have several what I call "containers" that
map C data structures to Key/Value lists and back again. One of the
features I've found most handy are routines evaluates a script over
the entire table of data structures I have loaded.

For a trivial example of what's going on at the Tcl level:

set fout [open dumpfile.csv w]
entity foreach { puts $fout [join [list $id $typeid
$object_shape] ,] }
close $fout

I'm exploring using NRE to implement the foreach method. (I have a
reference implementation that uses Tcl_EvalObj). And thusfar I've
gotten it to play well with NRE. Except for one annoying show stopper.
NRE only seems to pay attention to the last item I've loaded as a
callback.

Here's the foreach method in C:




static int Entity_nodeeval_finalize(
ClientData data[],
Tcl_Interp *interp,
int result
) {
Entity *p;
Tcl_Obj *pValueDict;
int i;
Tcl_Obj **varv;
int varc;

p=data[0];
pValueDict=data[1];

if (result == TCL_ERROR) {
Tcl_AddErrorInfo(interp, "\n (body of \"Entity with/foreach
\")");
}
if(result!=TCL_OK) {
if(pValueDict) {
Tcl_DecrRefCount(pValueDict);
}
return result;
}
if (pValueDict) {
int changed=0;
if (Tcl_ListObjGetElements(interp, pValueDict, &varc, &varv) !=
TCL_OK) {
return TCL_ERROR;
}
/*
** Read values back into the dict
** For now, we limit writeback to state variables
** And we don't care about garbage values
*/
for(i=0;i<varc;i+=2) {
Tcl_Obj *newValue;
int offset;
int type;
newValue=Tcl_ObjGetVar2(interp,varv[i],(Tcl_Obj *)NULL,0);
if(newValue==varv[i+1]) {
/* Undocumented, unsanctioned, but it works in practice
** If the pointer hasn't changed, neither has the value
*/
continue;
}
if(!newValue) {
/* Variable must have been unset... move along */
continue;
}
if( Entity_ValueOffset(0, varv[i], &offset, &type) == TCL_OK ) {
Entity_StructSet(interp,p,offset,newValue);
changed=1;
} else {
Entity_DictPut(p,varv[i],newValue);
}
}
if(changed) {
Entity_ApplySettings(p);
}
}
if(pValueDict) {
Tcl_DecrRefCount(pValueDict);
}
return TCL_OK;
}

/*
** title:
*/
int Entity_nodeeval(
Tcl_Interp *interp,
Entity *p,
Tcl_Obj *body
) {
Tcl_Obj *pValueDict=NULL,*pIDDict=NULL,*pTypeDict=NULL;
int i;
Tcl_Obj **varv;
int varc,result;

pValueDict=Entity_ToDict(interp,p);

if (Tcl_ListObjGetElements(interp, pValueDict, &varc, &varv) !=
TCL_OK) {
return TCL_ERROR;
}
for(i=0;i<varc;i+=2) {
Tcl_ObjSetVar2(interp, varv[i], (Tcl_Obj *)NULL, varv[i+1], 0);
}
pIDDict=Entity_Identify(p);
pTypeDict=Entity_TypeId(p);
Tcl_IncrRefCount(pValueDict);

Tcl_ObjSetVar2(interp,constant_stringObj(interp,"id"),NULL,pIDDict,
0);

Tcl_ObjSetVar2(interp,constant_stringObj(interp,"typeid"),NULL,pTypeDict,
0);

Tcl_NRAddCallback(interp, Entity_nodeeval_finalize, p, pValueDict,
NULL, NULL);

return Tcl_NREvalObj(interp, body, 0);
}

So what is happening is when I open that dumpfile.csv I see N (where N
is the number of objects in the container) lines of on element
repeated.

e212412,10121,box
e212412,10121,box
e212412,10121,box
e212412,10121,box
....

I'm guessing the problem is that my calls to Tcl_NRAddCallback are
replacing each other. Is that the way it's supposed to work?

--The Hypnotoad
From: Sean Woods on
After some discussion, it turns out the problem was actually upstream
of this C code.

I was trying to call NRE from an old-style c function linked with
Tcl_CreateCommand.

If the upstream function were linked to the interpreter with
Tcl_NRCreateCommand,
it probably would have worked. (Barring some additional stupidity of
mine in the
calling routines.)

On Aug 9, 6:02 pm, Sean Woods <y...(a)etoyoc.com> wrote:
> Well Tclers, I have an interesting problem, and feel free to tell me
> "Hypnotoad, that's not what NRE was designed for!"
>
> To speed up my Tcl code, I have several what I call "containers" that
> map C data structures to Key/Value lists and back again. One of the
> features I've found most handy are routines evaluates a script over
> the entire table of data structures I have loaded.
>
> For a trivial example of what's going on at the Tcl level:
>
> set fout [open dumpfile.csv w]
> entity foreach { puts $fout [join [list $id $typeid
> $object_shape] ,] }
> close $fout
>
> I'm exploring using NRE to implement the foreach method. (I have a
> reference implementation that uses Tcl_EvalObj). And thusfar I've
> gotten it to play well with NRE. Except for one annoying show stopper.
> NRE only seems to pay attention to the last item I've loaded as a
> callback.
>
> Here's the foreach method in C:
>
> static int  Entity_nodeeval_finalize(
>     ClientData data[],
>     Tcl_Interp *interp,
>     int result
> ) {
>   Entity *p;
>   Tcl_Obj *pValueDict;
>   int i;
>   Tcl_Obj **varv;
>   int varc;
>
>   p=data[0];
>   pValueDict=data[1];
>
>   if (result == TCL_ERROR) {
>     Tcl_AddErrorInfo(interp, "\n    (body of \"Entity with/foreach
> \")");
>   }
>   if(result!=TCL_OK) {
>     if(pValueDict) {
>       Tcl_DecrRefCount(pValueDict);
>     }
>     return result;
>   }
>   if (pValueDict) {
>     int changed=0;
>     if (Tcl_ListObjGetElements(interp, pValueDict, &varc, &varv) !=
> TCL_OK) {
>       return TCL_ERROR;
>     }
>     /*
>     ** Read values back into the dict
>     ** For now, we limit writeback to state variables
>     ** And we don't care about garbage values
>     */
>     for(i=0;i<varc;i+=2) {
>       Tcl_Obj *newValue;
>       int offset;
>       int type;
>       newValue=Tcl_ObjGetVar2(interp,varv[i],(Tcl_Obj *)NULL,0);
>       if(newValue==varv[i+1]) {
>          /* Undocumented, unsanctioned, but it works in practice
>          ** If the pointer hasn't changed, neither has the value
>          */
>          continue;
>       }
>       if(!newValue) {
>         /* Variable must have been unset... move along */
>         continue;
>       }
>       if( Entity_ValueOffset(0, varv[i], &offset, &type) == TCL_OK ) {
>           Entity_StructSet(interp,p,offset,newValue);
>           changed=1;
>       } else {
>         Entity_DictPut(p,varv[i],newValue);
>       }
>     }
>     if(changed) {
>       Entity_ApplySettings(p);
>     }
>   }
>   if(pValueDict) {
>     Tcl_DecrRefCount(pValueDict);
>   }
>   return TCL_OK;
>
> }
>
> /*
> ** title:
> */
> int Entity_nodeeval(
>   Tcl_Interp *interp,
>   Entity *p,
>   Tcl_Obj *body
> ) {
>   Tcl_Obj *pValueDict=NULL,*pIDDict=NULL,*pTypeDict=NULL;
>   int i;
>   Tcl_Obj **varv;
>   int varc,result;
>
>   pValueDict=Entity_ToDict(interp,p);
>
>   if (Tcl_ListObjGetElements(interp, pValueDict, &varc, &varv) !=
> TCL_OK) {
>       return TCL_ERROR;
>   }
>   for(i=0;i<varc;i+=2) {
>     Tcl_ObjSetVar2(interp, varv[i], (Tcl_Obj *)NULL, varv[i+1], 0);
>   }
>   pIDDict=Entity_Identify(p);
>   pTypeDict=Entity_TypeId(p);
>   Tcl_IncrRefCount(pValueDict);
>
>   Tcl_ObjSetVar2(interp,constant_stringObj(interp,"id"),NULL,pIDDict,
> 0);
>
> Tcl_ObjSetVar2(interp,constant_stringObj(interp,"typeid"),NULL,pTypeDict,
> 0);
>
>   Tcl_NRAddCallback(interp, Entity_nodeeval_finalize, p, pValueDict,
> NULL, NULL);
>
>   return Tcl_NREvalObj(interp, body, 0);
>
> }
>
> So what is happening is when I open that dumpfile.csv I see N (where N
> is the number of objects in the container) lines of on element
> repeated.
>
> e212412,10121,box
> e212412,10121,box
> e212412,10121,box
> e212412,10121,box
> ...
>
> I'm guessing the problem is that my calls to Tcl_NRAddCallback are
> replacing each other. Is that the way it's supposed to work?
>
> --The Hypnotoad

From: Robert Heller on
At Mon, 9 Aug 2010 15:47:15 -0700 (PDT) Sean Woods <yoda(a)etoyoc.com> wrote:

>
> After some discussion, it turns out the problem was actually upstream
> of this C code.
>
> I was trying to call NRE from an old-style c function linked with
> Tcl_CreateCommand.
>
> If the upstream function were linked to the interpreter with
> Tcl_NRCreateCommand,
> it probably would have worked. (Barring some additional stupidity of
> mine in the
> calling routines.)

I've implemented 'foreach' style loops over STL container objects (eg
STL <map>, <vector>, etc.) in C++, using TclObj commands. They are
interfaced to Tcl via SWIG (using C++/TclObj) and are part of my Model
Railroad System. They work quite well. The Model Railroad System is
open source and you are welcome to look at the code to see how I did
things. The code is even reasonably documented. The URL is in my
signature.

>
> On Aug 9, 6:02=A0pm, Sean Woods <y...(a)etoyoc.com> wrote:
> > Well Tclers, I have an interesting problem, and feel free to tell me
> > "Hypnotoad, that's not what NRE was designed for!"
> >
> > To speed up my Tcl code, I have several what I call "containers" that
> > map C data structures to Key/Value lists and back again. One of the
> > features I've found most handy are routines evaluates a script over
> > the entire table of data structures I have loaded.
> >
> > For a trivial example of what's going on at the Tcl level:
> >
> > set fout [open dumpfile.csv w]
> > entity foreach { puts $fout [join [list $id $typeid
> > $object_shape] ,] }
> > close $fout
> >
> > I'm exploring using NRE to implement the foreach method. (I have a
> > reference implementation that uses Tcl_EvalObj). And thusfar I've
> > gotten it to play well with NRE. Except for one annoying show stopper.
> > NRE only seems to pay attention to the last item I've loaded as a
> > callback.
> >
> > Here's the foreach method in C:
> >
> > static int =A0Entity_nodeeval_finalize(
> > =A0 =A0 ClientData data[],
> > =A0 =A0 Tcl_Interp *interp,
> > =A0 =A0 int result
> > ) {
> > =A0 Entity *p;
> > =A0 Tcl_Obj *pValueDict;
> > =A0 int i;
> > =A0 Tcl_Obj **varv;
> > =A0 int varc;
> >
> > =A0 p=3Ddata[0];
> > =A0 pValueDict=3Ddata[1];
> >
> > =A0 if (result =3D=3D TCL_ERROR) {
> > =A0 =A0 Tcl_AddErrorInfo(interp, "\n =A0 =A0(body of \"Entity with/foreac=
> h
> > \")");
> > =A0 }
> > =A0 if(result!=3DTCL_OK) {
> > =A0 =A0 if(pValueDict) {
> > =A0 =A0 =A0 Tcl_DecrRefCount(pValueDict);
> > =A0 =A0 }
> > =A0 =A0 return result;
> > =A0 }
> > =A0 if (pValueDict) {
> > =A0 =A0 int changed=3D0;
> > =A0 =A0 if (Tcl_ListObjGetElements(interp, pValueDict, &varc, &varv) !=3D
> > TCL_OK) {
> > =A0 =A0 =A0 return TCL_ERROR;
> > =A0 =A0 }
> > =A0 =A0 /*
> > =A0 =A0 ** Read values back into the dict
> > =A0 =A0 ** For now, we limit writeback to state variables
> > =A0 =A0 ** And we don't care about garbage values
> > =A0 =A0 */
> > =A0 =A0 for(i=3D0;i<varc;i+=3D2) {
> > =A0 =A0 =A0 Tcl_Obj *newValue;
> > =A0 =A0 =A0 int offset;
> > =A0 =A0 =A0 int type;
> > =A0 =A0 =A0 newValue=3DTcl_ObjGetVar2(interp,varv[i],(Tcl_Obj *)NULL,0);
> > =A0 =A0 =A0 if(newValue=3D=3Dvarv[i+1]) {
> > =A0 =A0 =A0 =A0 =A0/* Undocumented, unsanctioned, but it works in practic=
> e
> > =A0 =A0 =A0 =A0 =A0** If the pointer hasn't changed, neither has the valu=
> e
> > =A0 =A0 =A0 =A0 =A0*/
> > =A0 =A0 =A0 =A0 =A0continue;
> > =A0 =A0 =A0 }
> > =A0 =A0 =A0 if(!newValue) {
> > =A0 =A0 =A0 =A0 /* Variable must have been unset... move along */
> > =A0 =A0 =A0 =A0 continue;
> > =A0 =A0 =A0 }
> > =A0 =A0 =A0 if( Entity_ValueOffset(0, varv[i], &offset, &type) =3D=3D TCL=
> _OK ) {
> > =A0 =A0 =A0 =A0 =A0 Entity_StructSet(interp,p,offset,newValue);
> > =A0 =A0 =A0 =A0 =A0 changed=3D1;
> > =A0 =A0 =A0 } else {
> > =A0 =A0 =A0 =A0 Entity_DictPut(p,varv[i],newValue);
> > =A0 =A0 =A0 }
> > =A0 =A0 }
> > =A0 =A0 if(changed) {
> > =A0 =A0 =A0 Entity_ApplySettings(p);
> > =A0 =A0 }
> > =A0 }
> > =A0 if(pValueDict) {
> > =A0 =A0 Tcl_DecrRefCount(pValueDict);
> > =A0 }
> > =A0 return TCL_OK;
> >
> > }
> >
> > /*
> > ** title:
> > */
> > int Entity_nodeeval(
> > =A0 Tcl_Interp *interp,
> > =A0 Entity *p,
> > =A0 Tcl_Obj *body
> > ) {
> > =A0 Tcl_Obj *pValueDict=3DNULL,*pIDDict=3DNULL,*pTypeDict=3DNULL;
> > =A0 int i;
> > =A0 Tcl_Obj **varv;
> > =A0 int varc,result;
> >
> > =A0 pValueDict=3DEntity_ToDict(interp,p);
> >
> > =A0 if (Tcl_ListObjGetElements(interp, pValueDict, &varc, &varv) !=3D
> > TCL_OK) {
> > =A0 =A0 =A0 return TCL_ERROR;
> > =A0 }
> > =A0 for(i=3D0;i<varc;i+=3D2) {
> > =A0 =A0 Tcl_ObjSetVar2(interp, varv[i], (Tcl_Obj *)NULL, varv[i+1], 0);
> > =A0 }
> > =A0 pIDDict=3DEntity_Identify(p);
> > =A0 pTypeDict=3DEntity_TypeId(p);
> > =A0 Tcl_IncrRefCount(pValueDict);
> >
> > =A0 Tcl_ObjSetVar2(interp,constant_stringObj(interp,"id"),NULL,pIDDict,
> > 0);
> >
> > Tcl_ObjSetVar2(interp,constant_stringObj(interp,"typeid"),NULL,pTypeDict,
> > 0);
> >
> > =A0 Tcl_NRAddCallback(interp, Entity_nodeeval_finalize, p, pValueDict,
> > NULL, NULL);
> >
> > =A0 return Tcl_NREvalObj(interp, body, 0);
> >
> > }
> >
> > So what is happening is when I open that dumpfile.csv I see N (where N
> > is the number of objects in the container) lines of on element
> > repeated.
> >
> > e212412,10121,box
> > e212412,10121,box
> > e212412,10121,box
> > e212412,10121,box
> > ...
> >
> > I'm guessing the problem is that my calls to Tcl_NRAddCallback are
> > replacing each other. Is that the way it's supposed to work?
> >
> > --The Hypnotoad
>
>

--
Robert Heller -- 978-544-6933
Deepwoods Software -- Download the Model Railroad System
http://www.deepsoft.com/ -- Binaries for Linux and MS-Windows
heller(a)deepsoft.com -- http://www.deepsoft.com/ModelRailroadSystem/

From: Alexandre Ferrieux on
On Aug 10, 12:02 am, Sean Woods <y...(a)etoyoc.com> wrote:
> Well Tclers, I have an interesting problem, and feel free to tell me
> "Hypnotoad, that's not what NRE was designed for!"
>

Hypnotoad, that's not what NRE was designed for ;-)

Joke aside, are you sure NR-enabling will give you a huge speed ? Or
do you have realistic scenarios with deeply nested operators ?

-Alex