From: sam appleton on
Hi

I am trying to callback a TCL function from C. TCL calls my C function
with a Tcl_Obj* that represents the function to be called back with
certain arguments. When I'm ready to call the TCL function back,
I do

Tcl_Obj* callbackObj = (argument to C function from TCL call, let's
say "callback_test")


Tcl_Obj* funcArg = xxxx;
Tcl_Obj& cmd[2];

cmd[0] = callbackObj;
cmd[1] = funcArg;

Tcl_EvalObjv(interp,2,cmd,0)

when I do this, all I get is

expected integer but got "callback_test"


can anyone enlighten me on how to callback TCL from C with functions &
arguments?

Sam
From: Robert Heller on
At Wed, 4 Aug 2010 18:47:13 -0700 (PDT) sam appleton <sam.s.appleton(a)gmail.com> wrote:

>
> Hi
>
> I am trying to callback a TCL function from C. TCL calls my C function
> with a Tcl_Obj* that represents the function to be called back with
> certain arguments. When I'm ready to call the TCL function back,
> I do
>
> Tcl_Obj* callbackObj = (argument to C function from TCL call, let's
> say "callback_test")
>
>
> Tcl_Obj* funcArg = xxxx;
> Tcl_Obj& cmd[2];
>
> cmd[0] = callbackObj;
> cmd[1] = funcArg;
>
> Tcl_EvalObjv(interp,2,cmd,0)
>
> when I do this, all I get is
>
> expected integer but got "callback_test"
>
>
> can anyone enlighten me on how to callback TCL from C with functions &
> arguments?

Download my Model Railroad System (see my signature for the URL) for
some C++ code that does exactly that (look in both the FCFSupport and
TTSupport C++ directories of the sources.

Here is a code fragment. You want to look at the code for the
Tcl8WorkInProgressCallback member functions. (This code uses the C++
STL, including basic_strings.)


/** @brief Various callback classes.
* These classes are used to provide a means for various class members
* to access code in the outer application to handle message passing and
* related activies. For the most part, the base classes don't do anything
* at all, but provide a set of virtual methods that implement the various
* sorts of callback functionallity.
*
* @author Robert Heller \<heller\@deepsoft.com\>
*/

/** @addtogroup FCFSupport
* @{
*/

namespace FCFSupport {

/** @brief Work In Progress Callback.
*
* Provides a callback to manage a work in
* progress display. This class is a dummy base class. Applications
* can define member functions that manage an application specific
* work in progress display.
*
* @author Robert Heller \<heller\@deepsoft.com\>
*/
class WorkInProgressCallback {
public:
/** @brief Constructor.
* The base constructor does nothing. It is presumed that
* a derived class might do something useful.
*/
WorkInProgressCallback() {}
/** @brief Destructor.
* The base destructor does nothing. It is presumed that a
* derived class might do something useful.
*/
virtual ~WorkInProgressCallback() {}
#ifndef SWIG
/** Start up the work in progress display. An initial message
* is passed to be displayed.
* @param Message An initial message string.
*/
virtual void ProgressStart(const string Message) const {}
/** Update the progress meter. Advance the progress meter to the
* percent completed and display an updated message describing the
* progress.
* @param Percent The completion percentage, between 0 and 100.
* A value of 100 indicates that the job is done.
* @param Message A message to display, typically something
* identifing what tasks have been completed.
*/
virtual void ProgressUpdate(int Percent,const string Message) const {}
/** Mark the process meter as done. Forces the meter to 100 percent
* and display a work completion message.
* @param Message A message to display.
*/
virtual void ProgressDone(const string Message) const {}
#endif
};


#ifdef SWIG
class Tcl8WorkInProgressCallback : public FCFSupport::WorkInProgressCallback {
public:
Tcl8WorkInProgressCallback(Tcl_Interp *interp,const char *start_,
const char *update_, const char *done_) {}
virtual ~Tcl8WorkInProgressCallback() {}
};

%{
namespace FCFSupport {

/** @brief A Swig Tcl 8.x derived class for work in progress handling.
* Provides a Tcl interface to the work in progress callback
* handling code.
*/
class Tcl8WorkInProgressCallback : public WorkInProgressCallback {
public:
/** @args startScript updateScript doneScript
* Constructor. Creates a work in progress callback structure to
* call back Tcl code. Stores the three commands that implement
* the Tcl code for the callback.
* @param startScript Start prodedure. This command gets one argument,
* the message string for the work in progress startup.
* @param updateScript Update prodedure. This command gets two
* arguments, the percent done (as an integer between 0 and
* 100), and an update message string.
* @param doneScript Done procedure. This command gets one argument,
* the done message string.
*/
Tcl8WorkInProgressCallback(Tcl_Interp *interp_,const char *start_,
const char *update_,const char *done_) {
interp = interp_;
start = start_;
update = update_;
done = done_;
}
/*+ Destructor.
*/
virtual ~Tcl8WorkInProgressCallback() {}
/*+ Startup member function.
* @param Message Startup message.
*/
virtual void ProgressStart(const string Message) const;
/*+ Update member function.
* @param Percent Percent done, 0 to 100.
* @param Message Update message.
*/
virtual void ProgressUpdate(int Percent,const string Message) const;
/*+ Done member function.
* @param Message Completion message.
*/
virtual void ProgressDone(const string Message) const;
private:
/*+ Interpreter to use for Tcl callbacks.
*/
Tcl_Interp *interp;
/*+ Start procedure or command.
*/
string start;
/*+ Update procedure or command.
*/
string update;
/*+ Done procedure or command.
*/
string done;
};

void Tcl8WorkInProgressCallback::ProgressStart(const string Message) const {
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressStart(" << Message << ")" << endl;
#endif
Tcl_Obj *striptObj = Tcl_NewListObj(0,NULL);
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)start.c_str(),-1)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)Message.c_str(),-1)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressStart: striptObj is " << Tcl_GetStringFromObj(striptObj,NULL) << endl;
#endif
int result = Tcl_EvalObjEx(interp,striptObj,TCL_EVAL_GLOBAL);
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressStart: result = " << result << endl;
#endif
if (result != TCL_OK) Tcl_BackgroundError(interp);
}

void Tcl8WorkInProgressCallback::ProgressUpdate(int Percent,const string Message) const {
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressUpdate(" << Percent << "," << Message << ")" << endl;
#endif
Tcl_Obj *striptObj = Tcl_NewListObj(0,NULL);
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)update.c_str(),-1)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewIntObj(Percent)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)Message.c_str(),-1)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressUpdate: striptObj is " << Tcl_GetStringFromObj(striptObj,NULL) << endl;
#endif
int result = Tcl_EvalObjEx(interp,striptObj,TCL_EVAL_GLOBAL);
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressUpdate: result = " << result << endl;
#endif
if (result != TCL_OK) Tcl_BackgroundError(interp);
}

void Tcl8WorkInProgressCallback::ProgressDone(const string Message) const {
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressDone(" << Message << ")" << endl;
#endif
Tcl_Obj *striptObj = Tcl_NewListObj(0,NULL);
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)done.c_str(),-1)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)Message.c_str(),-1)) != TCL_OK) {
Tcl_BackgroundError(interp);
}
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressDone: striptObj is " << Tcl_GetStringFromObj(striptObj,NULL) << endl;
#endif
int result = Tcl_EvalObjEx(interp,striptObj,TCL_EVAL_GLOBAL);
#ifdef DEBUG
cerr << "*** Tcl8WorkInProgressCallback::ProgressDone: result = " << result << endl;
#endif
if (result != TCL_OK) Tcl_BackgroundError(interp);
}
}
%}



>
> Sam
>

--
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: Harald Oehlmann on
On 5 Aug., 03:47, sam appleton <sam.s.apple...(a)gmail.com> wrote:
> Tcl_Obj* callbackObj = (argument to C function from TCL call, let's
> say "callback_test")
> Tcl_Obj* funcArg = xxxx;
> Tcl_Obj& cmd[2];
> cmd[0] = callbackObj;
> cmd[1] = funcArg;
> Tcl_EvalObjv(interp,2,cmd,0)

Hi Sam,
for me, your approach and code sketch is correct.

> when I do this, all I get is
> expected integer but got "callback_test"

I did not get the issue. What is not working ?

Maybee you may post more code and more results ?
From: sam appleton on

Thanks for your reply Robert.
Are you saying the correct way to do this is to construct a ListObject
and pass that to Tcl_EvalObjEx? That's what your code looks to be
doing.

sam


On Aug 4, 7:14 pm, Robert Heller <hel...(a)deepsoft.com> wrote:
> At Wed, 4 Aug 2010 18:47:13 -0700 (PDT) sam appleton <sam.s.apple...(a)gmail.com> wrote:
>
>
>
>
>
> > Hi
>
> > I am trying to callback a TCL function from C. TCL calls my C function
> > with a Tcl_Obj* that represents the function to be called back with
> > certain arguments. When I'm ready to call the TCL function back,
> > I do
>
> > Tcl_Obj* callbackObj = (argument to C function from TCL call, let's
> > say "callback_test")
>
> > Tcl_Obj* funcArg = xxxx;
> > Tcl_Obj& cmd[2];
>
> > cmd[0] = callbackObj;
> > cmd[1] = funcArg;
>
> > Tcl_EvalObjv(interp,2,cmd,0)
>
> > when I do this, all I get is
>
> > expected integer but got "callback_test"
>
> > can anyone enlighten me on how to callback TCL from C with functions &
> > arguments?
>
> Download my Model Railroad System (see my signature for the URL) for
> some C++ code that does exactly that (look in both the FCFSupport and
> TTSupport C++ directories of the sources.
>
> Here is a code fragment.  You want to look at the code for the
> Tcl8WorkInProgressCallback member functions.  (This code uses the C++
> STL, including basic_strings.)
>
> /** @brief  Various callback classes.
>   *     These classes are used to provide a means for various class members
>   *     to access code in the outer application to handle message passing and
>   *     related activies. For the most part, the base classes don't do anything
>   *     at all, but provide a set of virtual methods that implement the various
>   *     sorts of callback functionallity.
>   *
>   *     @author Robert Heller \<heller\@deepsoft.com\>
>   */
>
> /** @addtogroup FCFSupport
>   * @{
>   */
>
> namespace FCFSupport {
>
> /**   @brief Work In Progress Callback.
>   *
>   *     Provides a callback to manage a work in
>   *     progress display.  This class is a dummy base class.  Applications
>   *     can define member functions that manage an application specific
>   *     work in progress display.
>   *
>   *     @author Robert Heller \<heller\@deepsoft.com\>
>   */
> class WorkInProgressCallback {
> public:
>         /** @brief Constructor.
>           * The base constructor does nothing.  It is presumed that
>           * a derived class might do something useful.
>           */
>         WorkInProgressCallback() {}
>         /** @brief Destructor.
>           * The base destructor does nothing.  It is presumed that a
>           * derived class might do something useful.
>           */
>         virtual ~WorkInProgressCallback() {}
> #ifndef SWIG
>         /**  Start up the work in progress display.  An initial message
>           * is passed to be displayed.
>           * @param Message An initial message string.
>           */
>         virtual void ProgressStart(const string Message) const {}
>         /**  Update the progress meter. Advance the progress meter to the
>           * percent completed and display an updated message describing the
>           * progress.
>           * @param Percent The completion percentage, between 0 and 100.
>           *       A value of 100 indicates that the job is done.
>           * @param Message A message to display, typically something
>           *       identifing what tasks have been completed.
>           */
>         virtual void ProgressUpdate(int Percent,const string Message) const {}
>         /**  Mark the process meter as done.  Forces the meter to 100 percent
>           * and display a work completion message.
>           * @param Message A message to display.
>           */
>         virtual void ProgressDone(const string Message) const {}
> #endif
>
> };
>
> #ifdef SWIG
> class Tcl8WorkInProgressCallback : public FCFSupport::WorkInProgressCallback {
> public:
>         Tcl8WorkInProgressCallback(Tcl_Interp *interp,const char *start_,
>                                    const char *update_, const char *done_) {}
>         virtual ~Tcl8WorkInProgressCallback() {}
>
> };
>
> %{
> namespace FCFSupport {
>
> /**  @brief A Swig Tcl 8.x derived class for work in progress handling.
>   *  Provides a Tcl interface to the work in progress callback
>   *  handling code.
>   */
> class Tcl8WorkInProgressCallback : public WorkInProgressCallback {
> public:
>         /** @args startScript updateScript doneScript
>           * Constructor.  Creates a work in progress callback structure to
>           * call back Tcl code.  Stores the three commands that implement
>           * the Tcl code for the callback.
>           * @param startScript Start prodedure. This command gets one argument,
>           *       the message string for the work in progress startup.
>           * @param updateScript Update prodedure. This command gets two
>           *       arguments, the percent done (as an integer between 0 and
>           *       100), and an update message string.
>           * @param doneScript Done procedure.  This command gets one argument,
>           *       the done message string.
>           */
>         Tcl8WorkInProgressCallback(Tcl_Interp *interp_,const char *start_,
>                 const char *update_,const char *done_) {
>                 interp = interp_;
>                 start  = start_;
>                 update = update_;
>                 done   = done_;
>         }
>         /*+  Destructor.
>           */
>         virtual ~Tcl8WorkInProgressCallback() {}
>         /*+  Startup member function.
>           * @param Message Startup message.
>           */
>         virtual void ProgressStart(const string Message) const;
>         /*+  Update member function.
>           * @param Percent Percent done, 0 to 100.
>           * @param Message Update message.
>           */
>         virtual void ProgressUpdate(int Percent,const string Message) const;
>         /*+  Done member function.
>           * @param Message Completion message.
>           */
>         virtual void ProgressDone(const string Message) const;
> private:
>         /*+  Interpreter to use for Tcl callbacks.
>           */
>         Tcl_Interp *interp;
>         /*+  Start procedure or command.
>           */
>         string start;
>         /*+  Update procedure or command.
>           */
>         string update;
>         /*+  Done procedure or command.
>           */
>         string done;    
>
> };
>
> void Tcl8WorkInProgressCallback::ProgressStart(const string Message) const {
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressStart(" << Message << ")" << endl;
> #endif
>         Tcl_Obj *striptObj = Tcl_NewListObj(0,NULL);
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)start.c_str(),-1)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)Message.c_str(),-1)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressStart: striptObj is " << Tcl_GetStringFromObj(striptObj,NULL) << endl;
> #endif
>         int result = Tcl_EvalObjEx(interp,striptObj,TCL_EVAL_GLOBAL);
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressStart: result = " << result << endl;
> #endif
>         if (result != TCL_OK) Tcl_BackgroundError(interp);
>
> }
>
> void Tcl8WorkInProgressCallback::ProgressUpdate(int Percent,const string Message) const {
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressUpdate(" << Percent << "," << Message << ")" << endl;
> #endif
>         Tcl_Obj *striptObj = Tcl_NewListObj(0,NULL);
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)update.c_str(),-1)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewIntObj(Percent)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)Message.c_str(),-1)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressUpdate: striptObj is " << Tcl_GetStringFromObj(striptObj,NULL) << endl;
> #endif
>         int result = Tcl_EvalObjEx(interp,striptObj,TCL_EVAL_GLOBAL);
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressUpdate: result = " << result << endl;
> #endif
>         if (result != TCL_OK) Tcl_BackgroundError(interp);
>
> }
>
> void Tcl8WorkInProgressCallback::ProgressDone(const string Message) const {
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressDone(" << Message << ")" << endl;
> #endif
>         Tcl_Obj *striptObj = Tcl_NewListObj(0,NULL);
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)done.c_str(),-1)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
>         if (Tcl_ListObjAppendElement(interp,striptObj,Tcl_NewStringObj((char *)Message.c_str(),-1)) != TCL_OK) {
>                 Tcl_BackgroundError(interp);
>         }
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressDone: striptObj is " << Tcl_GetStringFromObj(striptObj,NULL) << endl;
> #endif
>         int result = Tcl_EvalObjEx(interp,striptObj,TCL_EVAL_GLOBAL);
> #ifdef DEBUG
>         cerr << "*** Tcl8WorkInProgressCallback::ProgressDone: result = " << result << endl;
> #endif
>         if (result != TCL_OK) Tcl_BackgroundError(interp);}
> }
>
> %}
>
>
>
> > Sam
>
> --
> Robert Heller             -- 978-544-6933
> Deepwoods Software        -- Download the Model Railroad Systemhttp://www.deepsoft.com/ -- Binaries for Linux and MS-Windows
> hel...(a)deepsoft.com       --http://www.deepsoft.com/ModelRailroadSystem/

From: sam appleton on
Hi Harald - thanks for your reply. The frustrating thing is the
string that I quoted _was_ the result when the callback from C
to TCL occured via Tcl_EvalObjv, so it's hard to figure out exactly
what happened.


Sam


On Aug 4, 11:49 pm, Harald Oehlmann <wortka...(a)yahoo.de> wrote:
> On 5 Aug., 03:47, sam appleton <sam.s.apple...(a)gmail.com> wrote:
>
> > Tcl_Obj* callbackObj = (argument to C function from TCL call, let's
> > say "callback_test")
> > Tcl_Obj* funcArg = xxxx;
> > Tcl_Obj& cmd[2];
> > cmd[0] = callbackObj;
> > cmd[1] = funcArg;
> > Tcl_EvalObjv(interp,2,cmd,0)
>
> Hi Sam,
> for me, your approach and code sketch is correct.
>
> > when I do this, all I get is
> > expected integer but got "callback_test"
>
> I did not get the issue. What is not working ?
>
> Maybee you may post more code and more results ?