// // Copyright (C) 2004-2006, Maciej Sobczak // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // #ifndef CPPTCL_INCLUDED #define CPPTCL_INCLUDED #include #include #include #include #include #include #include #include namespace Tcl { namespace arg = std::placeholders; // exception class used for reporting all Tcl errors class tcl_error : public std::runtime_error { public: explicit tcl_error(std::string const &msg) : std::runtime_error(msg) {} explicit tcl_error(Tcl_Interp *interp) : std::runtime_error(Tcl_GetString(Tcl_GetObjResult(interp))) {} }; // call policies struct policies { policies() : variadic_(false) {} policies & factory(std::string const &name); // note: this is additive policies & sink(int index); policies & variadic(); std::string factory_; std::vector sinks_; bool variadic_; }; // syntax short-cuts policies factory(std::string const &name); policies sink(int index); policies variadic(); class interpreter; class object; namespace details { // wrapper for the evaluation result class result { public: result(Tcl_Interp *interp); operator bool() const; operator double() const; operator int() const; operator long() const; operator std::string() const; operator object() const; private: Tcl_Interp *interp_; }; // helper functions used to set the result value void set_result(Tcl_Interp *interp, bool b); void set_result(Tcl_Interp *interp, int i); void set_result(Tcl_Interp *interp, long i); void set_result(Tcl_Interp *interp, double d); void set_result(Tcl_Interp *interp, std::string const &s); void set_result(Tcl_Interp *interp, void *p); void set_result(Tcl_Interp *interp, object const &o); // helper functor for converting Tcl objects to the given type #include "details/conversions.h" // dispatchers able to capture (or ignore) the result #include "details/dispatchers.h" // helper for checking for required number of parameters // (throws tcl_error when not met) void check_params_no(int objc, int required); // helper for gathering optional params in variadic functions object get_var_params(Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[], int from, policies const &pol); // the callback_base is used to store callback handlers in a polynorphic map class callback_base { public: virtual ~callback_base() {} virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[], policies const &pol) = 0; }; // base class for object command handlers // and for class handlers class object_cmd_base { public: // destructor not needed, but exists to shut up the compiler warnings virtual ~object_cmd_base() {} virtual void invoke(void *p, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[], policies const &pol) = 0; }; // base class for all class handlers, still abstract class class_handler_base : public object_cmd_base { public: typedef std::map policies_map_type; class_handler_base(); void register_method(std::string const &name, std::shared_ptr ocb, policies const &p); policies & get_policies(std::string const &name); protected: typedef std::map< std::string, std::shared_ptr > method_map_type; // a map of methods for the given class method_map_type methods_; policies_map_type policies_; }; // class handler - responsible for executing class methods template class class_handler : public class_handler_base { public: virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[], policies const &pol) { C * p = static_cast(pv); if (objc < 2) { throw tcl_error("Too few arguments."); } std::string methodName(Tcl_GetString(objv[1])); if (methodName == "-delete") { Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); delete p; return; } // dispatch on the method name method_map_type::iterator it = methods_.find(methodName); if (it == methods_.end()) { throw tcl_error("Method " + methodName + " not found."); } it->second->invoke(pv, interp, objc, objv, pol); } }; // factory functions for creating class objects #include "details/constructors.h" // actual callback envelopes #include "details/callbacks.h" // actual method envelopes #include "details/methods.h" // helper meta function for figuring appropriate constructor callback #include "details/metahelpers.h" // this class is used to provide the "def" interface for defining // class member functions template class class_definer { public: class_definer(std::shared_ptr > ch) : ch_(ch) {} template class_definer & def(std::string const &name, R (C::*f)(), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method0(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)() const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method0(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method1(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method1(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method2(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method2(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method3(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method3(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method4(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method4(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method5(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method5(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method6(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method6(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6, T7), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method7(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6, T7) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method7(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6, T7, T8), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method8(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6, T7, T8) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method8(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6, T7, T8, T9), policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method9(f)), p); return *this; } template class_definer & def(std::string const &name, R (C::*f)(T1, T2, T3, T4, T5, T6, T7, T8, T9) const, policies const &p = policies()) { ch_->register_method(name, std::shared_ptr( new details::method9(f)), p); return *this; } private: std::shared_ptr > ch_; }; } // namespace details // init type for defining class constructors template class init {}; // no_init type and object - to define classes without constructors namespace details { struct no_init_type {}; } // namespace details extern details::no_init_type no_init; // interpreter wrapper class interpreter { public: interpreter(); interpreter(Tcl_Interp *, bool owner = true); ~interpreter(); void make_safe(); Tcl_Interp * get() const { return interp_; } // free function definitions template void def(std::string const &name, R (*f)(), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback0(f)), p); } template void def(std::string const &name, R (*f)(T1), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback1(f)), p); } template void def(std::string const &name, R (*f)(T1, T2), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback2(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback3(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3, T4), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback4(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3, T4, T5), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback5(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3, T4, T5, T6), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback6(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3, T4, T5, T6, T7), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback7(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3, T4, T5, T6, T7, T8), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback8(f)), p); } template void def(std::string const &name, R (*f)(T1, T2, T3, T4, T5, T6, T7, T8, T9), policies const &p = policies()) { add_function(name, std::shared_ptr( new details::callback9(f)), p); } // class definitions template details::class_definer class_(std::string const &name) { std::shared_ptr > ch( new details::class_handler()); add_class(name, ch); add_constructor(name, ch, std::shared_ptr( new details::callback0(&details::construct< C, void, void, void, void, void, void, void, void, void>::doit))); return details::class_definer(ch); } template details::class_definer class_(std::string const &name, init const &, policies const &p = policies()) { typedef typename details::get_callback_type_for_construct< C, T1, T2, T3, T4, T5, T6, T7, T8, T9>::type callback_type; std::shared_ptr > ch( new details::class_handler()); add_class(name, ch); add_constructor(name, ch, std::shared_ptr( new callback_type(&details::construct< C, T1, T2, T3, T4, T5, T6, T7, T8, T9>::doit)), p); return details::class_definer(ch); } template details::class_definer class_( std::string const &name, details::no_init_type const &) { std::shared_ptr > ch( new details::class_handler()); add_class(name, ch); return details::class_definer(ch); } // free script evaluation details::result eval(std::string const &script); details::result eval(std::istream &s); details::result eval(object const &o); // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result eval(InputIterator first, InputIterator last); // create alias from the *this interpreter to the target interpreter void create_alias(std::string const &cmd, interpreter &targetInterp, std::string const &targetCmd); // register a package info (useful when defining packages) void pkg_provide(std::string const &name, std::string const &version); // helper for cleaning up callbacks in non-managed interpreters static void clear_definitions(Tcl_Interp *); private: // copy not supported interpreter(const interpreter &); void operator=(const interpreter &); void add_function(std::string const &name, std::shared_ptr cb, policies const &p = policies()); void add_class(std::string const &name, std::shared_ptr chb); void add_constructor(std::string const &name, std::shared_ptr chb, std::shared_ptr cb, policies const &p = policies()); Tcl_Interp *interp_; bool owner_; }; // object wrapper class object { public: // constructors object(); explicit object(bool b); object(char const *buf, size_t size); // byte array explicit object(double b); explicit object(int i); // list creation // the InputIterator should give object& or Tcl_Obj* when dereferenced template object(InputIterator first, InputIterator last) : interp_(0) { std::vector v; fill_vector(v, first, last); obj_ = Tcl_NewListObj(static_cast(v.size()), v.empty() ? NULL : &v[0]); Tcl_IncrRefCount(obj_); } explicit object(long i); explicit object(char const *s); // string construction explicit object(std::string const &s); // string construction explicit object(Tcl_Obj *o, bool shared = false); object(object const &other, bool shared = false); ~object(); // assignment object & assign(bool b); object & resize(size_t size); // byte array resize object & assign(char const *buf, size_t size); // byte array assignment object & assign(double d); object & assign(int i); // list assignment // the InputIterator should give Tcl_Obj* or object& when dereferenced template object & assign(InputIterator first, InputIterator last) { std::vector v; fill_vector(v, first, last); Tcl_SetListObj(obj_, static_cast(v.size()), v.empty() ? NULL : &v[0]); return *this; } object & assign(long l); object & assign(char const *s); // string assignment object & assign(std::string const &s); // string assignment object & assign(object const &o); object & assign(Tcl_Obj *o); object & operator=(bool b) { return assign(b); } object & operator=(double d) { return assign(d); } object & operator=(int i) { return assign(i); } object & operator=(long l) { return assign(l); } object & operator=(char const *s) { return assign(s); } object & operator=(std::string const &s) { return assign(s); } object & operator=(object const &o) { return assign(o); } object & swap(object &other); // (logically) non-modifying members template T get(interpreter &i) const; char const * get() const; // string get char const * get(size_t &size) const; // byte array get size_t length(interpreter &i) const; // returns list length object at(interpreter &i, size_t index) const; Tcl_Obj * get_object() const { return obj_; } // modifying members object & append(interpreter &i, object const &o); object & append_list(interpreter &i, object const &o); // list replace // the InputIterator should give Tcl_Obj* or object& when dereferenced template object & replace(interpreter &i, size_t index, size_t count, InputIterator first, InputIterator last) { std::vector v; fill_vector(v, first, last); int res = Tcl_ListObjReplace(i.get(), obj_, static_cast(index), static_cast(count), static_cast(v.size()), v.empty() ? NULL : &v[0]); if (res != TCL_OK) { throw tcl_error(i.get()); } return *this; } object & replace(interpreter &i, size_t index, size_t count, object const &o); object & replace_list(interpreter &i, size_t index, size_t count, object const &o); // helper functions for piggy-backing interpreter info void set_interp(Tcl_Interp *interp); Tcl_Interp * get_interp() const; // helper function, also used from interpreter::eval template static void fill_vector(std::vector &v, InputIterator first, InputIterator last) { for (; first != last; ++first) { object o(*first, true); v.push_back(o.obj_); } } private: // helper function used from copy constructors void init(Tcl_Obj *o, bool shared); Tcl_Obj *obj_; Tcl_Interp *interp_; }; // available specializations for object::get template <> bool object::get(interpreter &i) const; template <> double object::get(interpreter &i) const; template <> int object::get(interpreter &i) const; template <> long object::get(interpreter &i) const; template <> char const * object::get(interpreter &i) const; template <> std::string object::get(interpreter &i) const; template <> std::vector object::get >(interpreter &i) const; // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result interpreter::eval(InputIterator first, InputIterator last) { std::vector v; object::fill_vector(v, first, last); int cc = Tcl_EvalObjv(interp_, static_cast(v.size()), v.empty() ? NULL : &v[0], 0); if (cc != TCL_OK) { throw tcl_error(interp_); } return details::result(interp_); } namespace details { // additional callback envelopes for variadic functions #include "details/callbacks_v.h" // additional method envelopes for variadic methods #include "details/methods_v.h" } // namespace details } // namespace Tcl // macro for defining loadable module entry point // - used for extending Tcl interpreter #define CPPTCL_MODULE(name, i) void name##_cpptcl_Init(Tcl::interpreter &i); \ extern "C" int name##_Init(Tcl_Interp *interp) \ { \ Tcl::interpreter i(interp, false); \ name##_cpptcl_Init(i); \ return TCL_OK; \ } \ void name##_cpptcl_Init(Tcl::interpreter &i) #endif // CPPTCL_INCLUDED