Am 29.07.17 um 11:04 schrieb
in...@meshparts.de:
> Am Freitag, 28. Juli 2017 23:38:41 UTC+2 schrieb Christian
> Gollwitzer:
> Thanks Christian for the detailed answer. Indeed, the constructor
> function is very elegant. But the wrapper with 500 LOC is somehow
> long (and perhaps slow?)
No, the wrapper is not slow. It will compile down to the same code as if
you had used the Tcl C API directly. Each SWDict or SWList only holds
one Tcl_Obj*, and in C++, for some cases like this, there is no other
storage. The functionality of this wrapper is resolved at compile time,
it does not impose any runtime cost.
> if you only want to return a Tcl list, isn't
> it? Second question: Is it possible to write the same wrapper in C?
This wrapper uses a bunch of tricks (templates, overloads, destructor
and copy semantics) that are impossible in C. BTW that was just a sketch
that I used in some internal extension. For sure it can be done much
better and more complete by a master of C++. There does exist such a
thing for Python, it's called Boost::Python.
Back to my wrapper, if you do
SWList l;
l.push_back(3);
l.push_back(2.7);
l.push_back("Hello");
it will construct a TclWideIntObj, a TclDoubleObj and a TclStringObj and
append it. The compiler decides that based on the given type. Impossible
in C.
> Then the constructor function would look similarly elegant?
C does not have any constructor functions. The direct Tcl C API is the
best what you can get, and you know how it looks like:
Tcl_Obj * l = Tcl_NewObj();
Tcl_ListObjAppendElement(NULL, l, Tcl_NewWideIntObj(3));
Tcl_ListObjAppendElement(NULL, l, Tcl_NewDoubleObj(2.7));
Tcl_ListObjAppendElement(NULL, l, Tcl_NewStringObj("Hello", -1));
The wrapper - if done properly - compiles to the exact same code.
Now consider that you append something with a function that can fail.
C++:
============
l.push_back(failing_int_func());
=============
In C++, a function can throw an exception, analogously to Tcl where an
error can be thrown. This will abort the insertion, and also abort the
whole function and by virtue of the destructors, call Tcl_DecrRef on all
objects which have been constructed so far and free them.
C:
==============
int result;
int code = failing_int_func(&result);
if (code != TCL_OK) goto error1;
Tcl_ListObjAppendElement(interp, l, Tcl_NewWideIntObj(l));
...
return TCL_OK;
error1:
Tcl_DecrRefCount(l);
return TCL_ERROR;
==============
You have to do all that manually.
Consider multiple parts which could fail, you have to keep all the
destruction in the error goto label in the correct sequence and in sync
.... The C++ compiler basically transforms the single line into the mess
that you see in the C version.
For simpler cases, like accepting / returning a single scalar value,
there exist programs which write all that interface code for you. One is
critcl, another one is SWIG. SWIG is way more powerful, because it
understands C++ (it reads the code) and allows you to introduce new
types. For example. in photoresize I enhanced SWIG to work on Tk photo
images. Critcl is useful for simple cases (short functions, scalar
values) and it knows how to drive the compiler.
Christian