/* see http://www.ishiboo.com/~nirva/Projects/tclcplusplus/ for more docs Put this code in C++: class TCLCPPCLASS(foo) { public: int method(int oc, Tcl_Obj *ov[]) { return TCL_OK; } TCLCPPOBJCONSTRUCTOR(foo) { RegisterMethod("method", &foo::method); } }; TclCPPObjFactory objcreator(interp, "FOO"); Now in TCL, you can do: set obj [::FOO::new] $obj method 1 2 3 and it'll call the C++ class foo's method() with 3 args, 1 2 3. You can also do: set obj [::FOO::new a b c] and the constructor will get called with 3 args, a b c. The TCLCPPOBJCONSTRUCTOR() macro will create 2 local vars: int oc, Tcl_Obj *ov[] which contain the 3 args passed to ::FOO::new */ #ifndef TCL_EXT_HELPER__H_ #define TCL_EXT_HELPER__H_ #ifdef WIN32 // C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning) #pragma warning(disable : 4800) // Win32 templates generate hundreds of "overly-long identifier" warnings #pragma warning(disable : 4786) #pragma warning(disable : 4503) #endif #include #include #include using namespace std; class _TclCPPObjFactory { protected: static unsigned int _tclcppobjid; static map _obj2name_map; static map _name2obj_map; public: _TclCPPObjFactory() { } virtual ~_TclCPPObjFactory() { } virtual void unregisterobj(const char *name) = 0; }; unsigned int _TclCPPObjFactory::_tclcppobjid = 0; map _TclCPPObjFactory::_obj2name_map; map _TclCPPObjFactory::_name2obj_map; template class TclCPPObjFactory : public _TclCPPObjFactory { Tcl_Interp *_interp; string _nmspc; static int __tclcreate(ClientData f, Tcl_Interp *interp, int oc, Tcl_Obj *const ov[]) { return static_cast *>(f)->_create(oc, ov); } static int __tclruncmd(ClientData f, Tcl_Interp *interp, int oc, Tcl_Obj *const ov[]) { return static_cast *>(f)->_run(oc, ov); } int _run(int oc, Tcl_Obj *const ov[]) { if (oc < 2) { Tcl_AddErrorInfo(_interp, "invalid arg count to command"); return TCL_ERROR; } char *name = Tcl_GetString(ov[0]); for (char *p = name; *p; p++) *p = tolower(*p); return ((OBJ*)_name2obj_map[Tcl_GetString(ov[0])])->run(oc-1, ov+1); } int _create(int oc, Tcl_Obj *const ov[]) { char buf[40]; sprintf(buf, "cppobj%x", _tclcppobjid++); string name = buf; OBJ *p = new OBJ(this, _interp, name.c_str(), _nmspc.c_str(), oc-1, ov+1); _obj2name_map[p] = name; _name2obj_map[name] = p; Tcl_Obj *objv = Tcl_NewStringObj(name.c_str(), name.size()); Tcl_SetListObj(Tcl_GetObjResult(_interp), 1, &objv); Tcl_CreateObjCommand(_interp, (char*)name.c_str(), __tclruncmd, this, 0); return TCL_OK; } public: // "::nmspc::new" returns prefix0, prefix1, etc.. TclCPPObjFactory(Tcl_Interp *interp, const char *nmspc) { _interp = interp; _nmspc = nmspc; string s = "::"; s += nmspc; s += "::new"; Tcl_CreateObjCommand(interp, (char*)s.c_str(), __tclcreate, this, 0); } virtual ~TclCPPObjFactory() { } void unregisterobj(const char *name) { int ret; if (_name2obj_map.find(name) == _name2obj_map.end()) return; if ((ret = Tcl_DeleteCommand(_interp, (char*)name)) != 0) fprintf(stderr, "COULD NOT REMOVE TCL COMMAND (of type %s) FROM INTERP: \"%s\", err(%d)\n", _nmspc.c_str(), (char*)name, ret); _obj2name_map.erase(_name2obj_map[name]); _name2obj_map.erase(name); } }; template class TclCPPObj { Tcl_Interp *_interp; string _tn; string _type; _TclCPPObjFactory *_factory; typedef int (OBJ::*memfunc)(int oc, Tcl_Obj * const ov[]); map _methods; protected: const char *get_tclname() { return _tn.c_str(); } const char *get_tcltype() { return _type.c_str(); } Tcl_Interp *get_interp() { return _interp; } void RegisterMethod(const char *name, memfunc fn) { _methods[name] = fn; } int method_destroy(int oc, Tcl_Obj * const ov[]) { if (oc != 0) { Tcl_AddErrorInfo(_interp, "invalid arg count to command"); return TCL_ERROR; } _factory->unregisterobj(_tn.c_str()); delete this; return TCL_OK; } public: TclCPPObj(_TclCPPObjFactory *factory, Tcl_Interp *i, const char *tn, const char *t) : _factory(factory), _interp(i), _tn(tn), _type(t) { RegisterMethod("destroy", &TclCPPObj::method_destroy); } virtual ~TclCPPObj() { } int run(int oc, Tcl_Obj * const ov[]) { if (_methods.find(Tcl_GetString(ov[0])) == _methods.end()) { Tcl_AddErrorInfo(_interp, "invalid graph command"); return TCL_ERROR; } memfunc fn = _methods[Tcl_GetString(ov[0])]; return (((OBJ*)this)->*(fn))(oc-1, ov+1); } }; #define TCLCPPOBJCONSTRUCTOR(cls) \ cls(_TclCPPObjFactory *factory, Tcl_Interp *interp, const char *tclname, \ const char *type, \ int oc, Tcl_Obj * const ov[]) : TclCPPObj(factory, interp, tclname, type) #define TCLCPPCLASS(cls) cls : public TclCPPObj #define TCLCPP_ARGS_EQ(cnt) do { \ if (oc != cnt) { \ Tcl_AddErrorInfo(get_interp(), "invalid arg count to command"); \ return TCL_ERROR; \ } } while (0) #endif