#ifndef __TclUtil_h_ #define __TclUtil_h_ #include #include #include #include #include // Convert a TCL string into a C++ std::string. std::string getString(Tcl_Obj *obj); // Convert a C++ std::string into a TCL string. Tcl_Obj *makeTclString(std::string const &s); inline Tcl_Obj *convertToTcl(std::string const &s) { return makeTclString(s); } inline Tcl_Obj *convertToTcl(uint64_t i) { return Tcl_NewLongObj(i); } class TclObjHolder; Tcl_Obj *convertToTcl(TclObjHolder const &v); template < class K, class V > Tcl_Obj *convertToTcl(std::map< K, V > const &v); template < class T > Tcl_Obj *convertToTcl(std::vector< T > const &v) { Tcl_Obj *result = Tcl_NewObj(); for (auto const &item : v) { Tcl_Obj *const itemAsTcl = convertToTcl(item); const int status = Tcl_ListObjAppendElement(NULL, result, itemAsTcl); assert(status == TCL_OK); } return result; } template < class K, class V > Tcl_Obj *convertToTcl(std::map< K, V > const &v) { Tcl_Obj *result = Tcl_NewDictObj(); for (auto const &kvp : v) Tcl_DictObjPut(NULL, result, convertToTcl(kvp.first), convertToTcl(kvp.second)); return result; } // Fill in a C++ data structure based on a TCL object. Returns TCL_OK on // success. On failure returns TCL_ERROR and puts an error message in interp. // interp can be NULL. int convertFromTcl(Tcl_Interp *interp, int &destination, Tcl_Obj *source); int convertFromTcl(Tcl_Interp *interp, bool &destination, Tcl_Obj *source); int convertFromTcl(Tcl_Interp *interp, double &destination, Tcl_Obj *source); int convertFromTcl(Tcl_Interp *interp, std::string &destination, Tcl_Obj *source); template < class T > int convertFromTcl(Tcl_Interp *interp, std::vector< T > &destination, Tcl_Obj *source); template < class K, class V > int convertFromTcl(Tcl_Interp *interp, std::map< K, V > &destination, Tcl_Obj *source); struct Void { }; inline int convertFromTcl(Tcl_Interp *interp, Void &destination, Tcl_Obj *source) { return TCL_OK; } inline int convertFromTcl(Tcl_Interp *interp, Tcl_Obj *&destination, Tcl_Obj *source) { destination = source; return TCL_OK; } class TclObjHolder; int convertFromTcl(Tcl_Interp *interp, TclObjHolder &destination, Tcl_Obj *source); /* class IConvertFromTcl { public: // "*this" is the destination. virtual int convertFromTcl(Tcl_Interp *interp, Tcl_Obj *source) =0; }; int convertFromTcl(Tcl_Interp *interp, IConvertFromTcl &destination, Tcl_Obj *source); class IConvertFromTclList { public: virtual int init(Tcl_Interp *interp, int count) { return TCL_OK; } virtual int add(Tcl_Interp *interp, Tcl_Obj *item) =0; virtual int commit(Tcl_Interp *interp) { return TCL_OK; } virtual void rollback() { }; int load(Tcl_Interp *interp, Tcl_Obj *source); }; inline int convertFromTcl(Tcl_Interp *interp, IConvertFromTclList &destination, Tcl_Obj *source) { return destination.load(interp, source); } class IConvertFromTclMap { virtual int init(Tcl_Interp *interp, int count) { return TCL_OK; } virtual int add(Tcl_Interp *interp, Tcl_Obj *key, Tcl_Obj *value) =0; virtual int commit(Tcl_Interp *interp) { return TCL_OK; } virtual void rollback() { }; }; int convertFromTcl(Tcl_Interp *interp, IConvertFromTclList &destination, Tcl_Obj *source); */ // Try to get a value from a dictionary and cast it to the output type. // If this succeeds, we return TCL_OK and set out to the right value. // If the key is missing, that is not an error. In that case we return // TCL_OK and nothing else changes. Any other error (i.e. not a valid // dictionary) will cause us to return TCL_ERROR and leave an error message // in interp. // // This is different from the way we read defaults in a lot of other places. // (I.e. dict_default_bool, and most of our XML helper classes.) This is // somewhat consistent with how options are normally handled in TCL. // // (Put the default value into out before calling dictGetIfExists(). This // will be left in place in case of any error.) int dictGetIfExists(Tcl_Interp *interp, Tcl_Obj *dict, std::string name, int &out); int dictGetIfExists(Tcl_Interp *interp, Tcl_Obj *dict, std::string name, long &out); int dictGetIfExists(Tcl_Interp *interp, Tcl_Obj *dict, std::string name, bool &out); int dictGetIfExists(Tcl_Interp *interp, Tcl_Obj *dict, std::string name, std::string &out); // This verifies the type of the TCL object, and copies the pointers from an // array into the given vectory. This does not increment any reference counts. // This seems obsolete. Look at the convertFromTcl() family of functions. int listToVector(Tcl_Interp *interp, Tcl_Obj *source, std::vector< Tcl_Obj * > &appendTo); // Automatically manages the reference counts for a TCL object. class TclObjHolder { private: Tcl_Obj *_obj; public: TclObjHolder(Tcl_Obj *obj = NULL) { _obj = obj; if (obj) Tcl_IncrRefCount(obj); } ~TclObjHolder() { if (_obj) Tcl_DecrRefCount(_obj); } TclObjHolder(TclObjHolder const &other) { _obj = other._obj; if (_obj) Tcl_IncrRefCount(_obj); } TclObjHolder(TclObjHolder &&other) { _obj = other._obj; other._obj = NULL; } void operator =(TclObjHolder const &other) { if (_obj) Tcl_DecrRefCount(_obj); _obj = other._obj; if (_obj) Tcl_IncrRefCount(_obj); } void operator =(TclObjHolder &&other) { _obj = other._obj; other._obj = NULL; } void operator =(Tcl_Obj *obj) { if (_obj) Tcl_DecrRefCount(_obj); _obj = obj; if (_obj) Tcl_IncrRefCount(_obj); } Tcl_Obj *getObj() const { return _obj; } operator bool() const { return _obj; } bool operator !() const { return !_obj; } }; inline Tcl_Obj *convertToTcl(TclObjHolder const &v) { return v.getObj(); } template < class T > int convertFromTcl(Tcl_Interp *interp, std::vector< T > &destination, Tcl_Obj *source) { destination.clear(); int count; Tcl_Obj **items; if (Tcl_ListObjGetElements(interp, source, &count, &items) != TCL_OK) return TCL_ERROR; destination.resize(count); for (int i = 0; i < count; i++) if (convertFromTcl(interp, destination[i], items[i]) != TCL_OK) { destination.clear(); return TCL_ERROR; } return TCL_OK; } template < class K, class V > int convertFromTcl(Tcl_Interp *interp, std::map< K, V > &destination, Tcl_Obj *source) { destination.clear(); Tcl_DictSearch search; Tcl_Obj *key, *value; int done; bool success = true; if (Tcl_DictObjFirst(interp, source, &search, &key, &value, &done) != TCL_OK) return TCL_ERROR; for (; !done ; Tcl_DictObjNext(&search, &key, &value, &done)) { // Convert the key object and store it into a temporary variable. K realKey; if (convertFromTcl(interp, realKey, key) != TCL_OK) { success = false; break; } // Convert the value object and store it directly into the map. if (convertFromTcl(interp, destination[realKey], value) != TCL_OK) { success = false; break; } } Tcl_DictObjDone(&search); if (success) return TCL_OK; destination.clear(); return TCL_ERROR; } class BreakAndSucceed { }; class BreakAndFail { }; template < class T > int tclListForEach(Tcl_Interp *interp, Tcl_Obj *list, T const &action) { int count; Tcl_Obj **items; if (Tcl_ListObjGetElements(interp, list, &count, &items) != TCL_OK) return TCL_ERROR; for (int i = 0; i < count; i++) try { action(items[i]); } catch (BreakAndSucceed) { return TCL_OK; } catch (BreakAndFail) { return TCL_ERROR; } return TCL_OK; } template < class T > int tclDictForEach(Tcl_Interp *interp, Tcl_Obj *dict, T const &action) { Tcl_DictSearch search; Tcl_Obj *key, *value; int done; if (Tcl_DictObjFirst(interp, dict, &search, &key, &value, &done) != TCL_OK) return TCL_ERROR; bool success = true; for (; !done ; Tcl_DictObjNext(&search, &key, &value, &done)) try { action(key, value); } catch (BreakAndSucceed) { break; } catch (BreakAndFail) { success = false; break; } catch (...) { Tcl_DictObjDone(&search); throw; } Tcl_DictObjDone(&search); return success?TCL_OK:TCL_ERROR; } template< class T = int, T ON_FAIL = (T)-1 > class GetTclIndex { private: std::vector< std::string > _asStrings; std::vector< char const * > _asCharStars; GetTclIndex(GetTclIndex const &) = delete; void operator =(GetTclIndex const &) = delete; public: GetTclIndex(std::vector< std::string > &&asStrings) : _asStrings(std::move(asStrings)) { _asCharStars.reserve(asStrings.size()); for (std::string const &s : _asStrings) _asCharStars.push_back(s.c_str()); assert(_asStrings.size() == _asCharStars.size()); } T get(Tcl_Interp *interp, Tcl_Obj *string, std::string const &message) { int index; int result = Tcl_GetIndexFromObj(interp, string, &_asCharStars[0], message.c_str(), /*flags = */ 0, &index); if (result != TCL_OK) return ON_FAIL; else return (T)index; } }; #endif