From: Lawrence Woodman on
Hello all,

I am having problems calling unexported methods from the C api. I have
the following C code to create a method called set2 in the BaseExample
class (this ends up as libset.so):

#include <string.h>
#include <tcl8.6/tclOO.h>

static int Set2(ClientData clientData, Tcl_Interp *interp,
Tcl_ObjectContext objectContext, int objc, Tcl_Obj *const objv[])
{
char buffer[200];

int argSkip = Tcl_ObjectContextSkippedArgs(objectContext);

if (objc != argSkip) {
Tcl_WrongNumArgs(interp, 1, objv, "");
return TCL_ERROR;
}

Tcl_Object contextObject = Tcl_ObjectContextObject(objectContext);
Tcl_Namespace *namespace = Tcl_GetObjectNamespace(contextObject);

if (strlen(namespace->fullName) >= 200-7) {
return TCL_ERROR;
}

/* If I try this line instead of the following then */
/* I get:- ERROR: unknown method "SetX"... */
/* sprintf(buffer, "%s SetX 2", namespace->fullName); */

/* The following generates:- ERROR: invalid command name "my" *
sprintf(buffer, "my SetX 2");

if (Tcl_EvalObjEx(interp, Tcl_NewStringObj(buffer, -1), 0)
!= TCL_OK) {
printf("ERROR: %s\n", Tcl_GetStringFromObj
(Tcl_GetObjResult(interp), NULL));
}


return TCL_OK;


}

int Set_Init(Tcl_Interp *interp)
{
const static Tcl_MethodType set2MethodType = {
TCL_OO_METHOD_VERSION_CURRENT, /* version */
"set2", /* name */
Set2, /* callProc */
NULL, /* deleteProc */
NULL /* cloneProc */
};

Tcl_Obj *baseName;
Tcl_Object baseObject;
Tcl_Class baseClass;
Tcl_Obj *set2Name;

// Get the root class
// Get the Validator class
baseName = Tcl_NewStringObj("BaseExample", -1);
Tcl_IncrRefCount(baseName);
if ((baseObject = Tcl_GetObjectFromObj(interp, baseName))
== NULL) {
Tcl_DecrRefCount(baseName);
return TCL_ERROR;
}
Tcl_DecrRefCount(baseName);
baseClass = Tcl_GetObjectAsClass(baseObject);

set2Name = Tcl_NewStringObj("set2", -1);
Tcl_IncrRefCount(set2Name);
Tcl_NewMethod( interp, baseClass, set2Name, 1, &set2MethodType,
(ClientData) NULL);
Tcl_DecrRefCount(set2Name);


if ( Tcl_PkgProvide(interp, "set", "0.1") != TCL_OK ) {
return TCL_ERROR;
}

return TCL_OK;
}


This is tested using the following script:

::oo::class create BaseExample {

constructor {} {
variable X 0
}

method SetX {pX} {
variable X $pX
}

method getX {} {
variable X
return $X
}
}

load ./set/libset.so

set base [BaseExample new]
puts "getX: [$base getX]"
$base set2
puts "getX: [$base getX]"




The code above has just been created to test and explain the problem I am
having. Which is essentially that depending on how I try to call the
unexported method, I either get: 'ERROR: unknown method "SetX"...' or
'ERROR: invalid command name "my"'.


Any help on this would be greatly appreciated.




Lawrence Woodman
From: Donal K. Fellows on
On Jul 24, 8:12 am, Lawrence Woodman <nos...(a)example.com> wrote:
> The code above has just been created to test and explain the problem I am
> having.  Which is essentially that depending on how I try to call the
> unexported method, I either get: 'ERROR: unknown method "SetX"...' or
> 'ERROR: invalid command name "my"'.

An unexported method is, well, not exported. It's not part of the
public interface of an object. If you wish to call it, you need to do
so via [my], but that's not a command that's available trivially
outside the body of a method. Why? Because it's not in the global
namespace (which knows nothing about objects). So where is it?

As you should be aware, every object has its own namespace. It's where
the object stores all its variables. You can (assuming you've got a
recent enough build of TclOO) find out the namespace's name from the
Tcl level with [info object namespace] and from the C level with
Tcl_GetObjectNamespace(). Aside from those variables, there's also one
command by default: [my]. Every object has its own. Getting the name
of a namespace is trivial. Calling any method through that route is
now trivial...

(BTW, the namespace name is also a stable reference to the object as
namespaces cannot be renamed or aliased.)

Donal.
From: Lawrence Woodman on
On Sun, 25 Jul 2010 08:33:00 -0700, Donal K. Fellows wrote:

> On Jul 24, 8:12 am, Lawrence Woodman <nos...(a)example.com> wrote:
>> The code above has just been created to test and explain the problem I
>> am having.  Which is essentially that depending on how I try to call
>> the unexported method, I either get: 'ERROR: unknown method "SetX"...'
>> or 'ERROR: invalid command name "my"'.
>
> An unexported method is, well, not exported. It's not part of the public
> interface of an object. If you wish to call it, you need to do so via
> [my], but that's not a command that's available trivially outside the
> body of a method. Why? Because it's not in the global namespace (which
> knows nothing about objects). So where is it?
>
> As you should be aware, every object has its own namespace. It's where
> the object stores all its variables. You can (assuming you've got a
> recent enough build of TclOO) find out the namespace's name from the Tcl
> level with [info object namespace] and from the C level with
> Tcl_GetObjectNamespace(). Aside from those variables, there's also one
> command by default: [my]. Every object has its own. Getting the name of
> a namespace is trivial. Calling any method through that route is now
> trivial...


Donal,

Thanks for your help. I assumed that Tcl_EvalObjEx() would be running in
the same namespace as the method within which it had been called and was
surprised to find out that it runs in the global namespace.

I can now access the unexported methods without any problem.


bfn


Lawrence Woodman
From: Donal K. Fellows on
On 27 July, 10:54, Lawrence Woodman <nos...(a)example.com> wrote:
> Thanks for your help.  I assumed that Tcl_EvalObjEx() would be running in
> the same namespace as the method within which it had been called and was
> surprised to find out that it runs in the global namespace.  

Eh? That conclusion sounds surprising to me, so I don't trust it.
(That you might be in a different context to what you expect though,
that's *very* possible.)

Donal.
From: Lawrence Woodman on
On Tue, 27 Jul 2010 15:55:00 -0700, Donal K. Fellows wrote:

> On 27 July, 10:54, Lawrence Woodman <nos...(a)example.com> wrote:
>> Thanks for your help.  I assumed that Tcl_EvalObjEx() would be running
>> in the same namespace as the method within which it had been called and
>> was surprised to find out that it runs in the global namespace.
>
> Eh? That conclusion sounds surprising to me, so I don't trust it. (That
> you might be in a different context to what you expect though, that's
> *very* possible.)

I'm guessing from your reply that I may have misunderstood this more than
I realized. If I run Tcl_EvaLObjEx() with "namspace current" from a
method that I have created, I get "::" returned, indicating that it is
running in the global namespace. Should this not happen?


bfn


Lawrence