From 2c8e302554a91763bde6b9db279f15d647691b67 Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Fri, 24 Sep 2021 21:06:52 +0200 Subject: [PATCH 01/10] prototype --- cpptcl.cc | 240 +++++++------- cpptcl/cpptcl.h | 566 ++++++++++++++++++++++++---------- cpptcl/details/bind.h | 175 ----------- cpptcl/details/callbacks.h | 215 ------------- cpptcl/details/callbacks_v.h | 201 ------------ cpptcl/details/constructors.h | 51 --- cpptcl/details/conversions.h | 67 ---- cpptcl/details/dispatchers.h | 89 ------ cpptcl/details/metahelpers.h | 31 -- cpptcl/details/methods.h | 304 ------------------ cpptcl/details/methods_v.h | 263 ---------------- 11 files changed, 525 insertions(+), 1677 deletions(-) delete mode 100644 cpptcl/details/bind.h delete mode 100644 cpptcl/details/callbacks.h delete mode 100644 cpptcl/details/callbacks_v.h delete mode 100644 cpptcl/details/constructors.h delete mode 100644 cpptcl/details/conversions.h delete mode 100644 cpptcl/details/dispatchers.h delete mode 100644 cpptcl/details/metahelpers.h delete mode 100644 cpptcl/details/methods.h delete mode 100644 cpptcl/details/methods_v.h diff --git a/cpptcl.cc b/cpptcl.cc index 9cef8ab..02cbb81 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -19,9 +19,20 @@ using namespace Tcl; using namespace Tcl::details; using namespace std; + +typedef std::pair, policies> callback_handler_client_data_t; +struct constructor_handler_client_data_t { + callback_base * cb; + shared_ptr chb; + policies pol; +}; + + result::result(Tcl_Interp *interp) : interp_(interp) {} result::operator bool() const { + return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); + Tcl_Obj *obj = Tcl_GetObjResult(interp_); int val, cc; @@ -34,6 +45,7 @@ result::operator bool() const { } result::operator double() const { + return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); Tcl_Obj *obj = Tcl_GetObjResult(interp_); double val; @@ -46,6 +58,7 @@ result::operator double() const { } result::operator int() const { + return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); Tcl_Obj *obj = Tcl_GetObjResult(interp_); int val, cc; @@ -58,6 +71,7 @@ result::operator int() const { } result::operator long() const { + return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); Tcl_Obj *obj = Tcl_GetObjResult(interp_); long val; @@ -118,6 +132,7 @@ object details::get_var_params(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv return o; } + namespace // anonymous { @@ -134,6 +149,11 @@ typedef map policies_map; policies_map call_policies; + + typedef std::map all_definitions2_t; + typedef std::map all_definitions_t; + all_definitions_t all_definitions; + // map of object handlers typedef map> class_interp_map; typedef map class_handlers_map; @@ -154,94 +174,18 @@ bool find_policies(Tcl_Interp *interp, string const &cmdName, policies_interp_ma extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); -// helper function for post-processing call policies -// for both free functions (isMethod == false) -// and class methods (isMethod == true) -void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod) { - // check if it is a factory - if (!pol.factory_.empty()) { - class_handlers_map::iterator it = class_handlers.find(interp); - - if (it == class_handlers.end()) { - throw tcl_error("Factory was registered for unknown class."); - } - - class_interp_map::iterator oit = it->second.find(pol.factory_); - if (oit == it->second.end()) { - throw tcl_error("Factory was registered for unknown class."); - } - - class_handler_base *chb = oit->second.get(); - - // register a new command for the object returned - // by this factory function - // if everything went OK, the result is the address of the - // new object in the 'pXXX' form - // - the new command will be created with this name - - Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(chb), 0); - } - - // process all declared sinks - // - unregister all object commands that envelopes the pointers - for (vector::iterator s = pol.sinks_.begin(); s != pol.sinks_.end(); ++s) { - if (isMethod == false) { - // example: if there is a declared sink at parameter 3, - // and the Tcl command was: - // % fun par1 par2 PAR3 par4 - // then the index 3 correctly points into the objv array - - int index = *s; - Tcl_DeleteCommand(interp, Tcl_GetString(objv[index])); - } else { - // example: if there is a declared sink at parameter 3, - // and the Tcl command was: - // % $p method par1 par2 PAR3 par4 - // then the index 3 needs to be incremented - // in order correctly point into the 4th index of objv array - - int index = *s + 1; - Tcl_DeleteCommand(interp, Tcl_GetString(objv[index])); - } - } -} // actual functions handling various callbacks // generic callback handler -extern "C" int callback_handler(ClientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - callback_map::iterator it = callbacks.find(interp); - - if (it == callbacks.end()) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke non-existent callback (wrong interpreter?)", -1)); - return TCL_ERROR; - } - - string cmdName(Tcl_GetString(objv[0])); - callback_interp_map::iterator iti = it->second.find(cmdName); - if (iti == it->second.end()) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke non-existent callback (wrong cmd name?)", -1)); - return TCL_ERROR; - } - - policies_map::iterator pit = call_policies.find(interp); - if (pit == call_policies.end()) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke callback with no known policies", -1)); - return TCL_ERROR; - } - - policies_interp_map::iterator piti; - if (find_policies(interp, cmdName, piti) == false) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke callback with no known policies", -1)); - return TCL_ERROR; - } - - policies &pol = piti->second; +extern "C" int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + // callback_handler_client_data_t * cdp = (callback_handler_client_data_t *) cd; + callback_base * cb = (callback_base *) cd; + try { - iti->second->invoke(interp, objc, objv, pol); - - post_process_policies(interp, pol, objv, false); + cb->invoke(interp, objc, objv); + //post_process_policies(interp, cdp->second, objv, false); } catch (exception const &e) { Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); return TCL_ERROR; @@ -276,7 +220,7 @@ extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_O chb->invoke(p, interp, objc, objv, pol); - post_process_policies(interp, pol, objv, true); + //post_process_policies(interp, pol, objv, true); } catch (exception const &e) { Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); return TCL_ERROR; @@ -294,37 +238,17 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, // which is responsible for managing commands for // objects of a given type - class_handler_base *chb = reinterpret_cast(cd); - callback_map::iterator it = constructors.find(interp); - if (it == constructors.end()) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke non-existent callback (wrong interpreter?)", -1)); - return TCL_ERROR; - } - - string className(Tcl_GetString(objv[0])); - callback_interp_map::iterator iti = it->second.find(className); - if (iti == it->second.end()) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke non-existent callback (wrong class name?)", -1)); - return TCL_ERROR; - } - - policies_interp_map::iterator piti; - if (find_policies(interp, className, piti) == false) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("Trying to invoke callback with no known policies", -1)); - return TCL_ERROR; - } - - policies &pol = piti->second; + constructor_handler_client_data_t * up = (constructor_handler_client_data_t *) cd; try { - iti->second->invoke(interp, objc, objv, pol); + up->cb->invoke(interp, objc, objv); // if everything went OK, the result is the address of the // new object in the 'pXXX' form // - we can create a new command with this name - Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(chb), 0); + Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(up->chb.get()), 0); } catch (exception const &e) { Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE); return TCL_ERROR; @@ -338,6 +262,10 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, } // namespace + + + + Tcl::details::no_init_type Tcl::no_init; policies &policies::factory(string const &name) { @@ -642,7 +570,7 @@ Tcl_Interp *object::get_interp() const { return interp_; } Tcl::interpreter *interpreter::defaultInterpreter = nullptr; -interpreter::interpreter() { +interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { interp_ = Tcl_CreateInterp(); owner_ = true; if (defaultInterpreter) { @@ -650,7 +578,7 @@ interpreter::interpreter() { } } -interpreter::interpreter(Tcl_Interp *interp, bool owner) { +interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_(nullptr), terr_(nullptr) { interp_ = interp; owner_ = owner; if (!defaultInterpreter) { @@ -662,7 +590,7 @@ interpreter::interpreter(Tcl_Interp *interp, bool owner) { } } -interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.owner_) {} +interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.owner_), tin_(nullptr), tout_(nullptr), terr_(nullptr) {} interpreter::~interpreter() { if (owner_) { @@ -754,7 +682,6 @@ void interpreter::create_namespace(string const &name) { } } - void interpreter::create_alias(string const &cmd, interpreter &targetInterp, string const &targetCmd) { int cc = Tcl_CreateAlias(interp_, cmd.c_str(), targetInterp.interp_, targetCmd.c_str(), 0, 0); if (cc != TCL_OK) { @@ -762,6 +689,18 @@ void interpreter::create_alias(string const &cmd, interpreter &targetInterp, str } } +void interpreter::clear_definitions(Tcl_Interp *interp) { + all_definitions_t::iterator it = all_definitions.find(interp); + if (it == all_definitions.end()) return; + for (all_definitions2_t::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { + Tcl_DeleteCommand(interp, it2->first.c_str()); + delete it2->second; + } + all_definitions.erase(it); + class_handlers.erase(interp); +} + +#if 0 void interpreter::clear_definitions(Tcl_Interp *interp) { // delete all callbacks that were registered for given interpreter @@ -808,21 +747,30 @@ void interpreter::clear_definitions(Tcl_Interp *interp) { class_handlers.erase(interp); } +#endif -void interpreter::add_function(string const &name, shared_ptr cb, policies const &p) { - Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, 0, 0); +void interpreter::add_function(string const &name, callback_base * cb) { + Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, cb, 0); + all_definitions[interp_][name] = cb; +} - callbacks[interp_][name] = cb; - call_policies[interp_][name] = p; +void interpreter::add_class(string const &name, shared_ptr chb) { + class_handlers[interp_][name] = chb; } -void interpreter::add_class(string const &name, shared_ptr chb) { class_handlers[interp_][name] = chb; } +void interpreter::add_constructor(string const &name, shared_ptr chb, callback_base * cb, policies const &p) { + constructor_handler_client_data_t * up = new constructor_handler_client_data_t; + up->cb = cb; + up->chb = chb; + up->pol = p; + + Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, up, 0); + all_definitions[interp_][name] = cb; -void interpreter::add_constructor(string const &name, shared_ptr chb, shared_ptr cb, policies const &p) { - Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, static_cast(chb.get()), 0); + //Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, static_cast(chb.get()), 0); - constructors[interp_][name] = cb; - call_policies[interp_][name] = p; + //constructors[interp_][name] = cb; + //call_policies[interp_][name] = p; } int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { @@ -845,6 +793,60 @@ long tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { return res; } +namespace Tcl { +// helper function for post-processing call policies +// for both free functions (isMethod == false) +// and class methods (isMethod == true) +void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod) { + // check if it is a factory + if (!pol.factory_.empty()) { + class_handlers_map::iterator it = class_handlers.find(interp); + + if (it == class_handlers.end()) { + throw tcl_error("Factory was registered for unknown class."); + } + + class_interp_map::iterator oit = it->second.find(pol.factory_); + if (oit == it->second.end()) { + throw tcl_error("Factory was registered for unknown class."); + } + + class_handler_base *chb = oit->second.get(); + + // register a new command for the object returned + // by this factory function + // if everything went OK, the result is the address of the + // new object in the 'pXXX' form + // - the new command will be created with this name + + Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(chb), 0); + } + + // process all declared sinks + // - unregister all object commands that envelopes the pointers + for (vector::iterator s = pol.sinks_.begin(); s != pol.sinks_.end(); ++s) { + if (isMethod == false) { + // example: if there is a declared sink at parameter 3, + // and the Tcl command was: + // % fun par1 par2 PAR3 par4 + // then the index 3 correctly points into the objv array + + int index = *s; + Tcl_DeleteCommand(interp, Tcl_GetString(objv[index])); + } else { + // example: if there is a declared sink at parameter 3, + // and the Tcl command was: + // % $p method par1 par2 PAR3 par4 + // then the index 3 needs to be incremented + // in order correctly point into the 4th index of objv array + + int index = *s + 1; + Tcl_DeleteCommand(interp, Tcl_GetString(objv[index])); + } + } +} +} + bool tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { int res; int cc = Tcl_GetBooleanFromObj(interp, obj, &res); diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index 7a748d6..35b8e66 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include // // Using TCL stubs is the default behavior @@ -82,6 +85,8 @@ policies usage(std::string const &message); class interpreter; class object; +void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod); + namespace details { // wrapper for the evaluation result @@ -110,12 +115,60 @@ 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 "cpptcl/details/conversions.h" +template struct tcl_cast; + +template struct tcl_cast { + static T *from(Tcl_Interp *, Tcl_Obj *obj, bool byReference) { + std::string s(Tcl_GetString(obj)); + if (s.size() == 0) { + throw tcl_error("Expected pointer value, got empty string."); + } + + if (s[0] != 'p') { + throw tcl_error("Expected pointer value."); + } + + std::istringstream ss(s); + char dummy; + void *p; + ss >> dummy >> p; + + return static_cast(p); + } +}; + +// the following partial specialization is to strip reference +// (it returns a temporary object of the underlying type, which +// can be bound to the const-ref parameter of the actual function) + +template struct tcl_cast { + static T from(Tcl_Interp *interp, Tcl_Obj *obj, bool byReference) { return tcl_cast::from(interp, obj, byReference); } +}; + +template class tcl_cast_by_reference { + public: + static bool const value = false; +}; + +// the following specializations are implemented -// dispatchers able to capture (or ignore) the result -#include "cpptcl/details/dispatchers.h" +template <> struct tcl_cast { static int from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; +template <> struct tcl_cast { static long from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; + +template <> struct tcl_cast { static bool from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; + +template <> struct tcl_cast { static double from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; + +template <> struct tcl_cast { static std::string from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; + +template <> struct tcl_cast { static char const *from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; + +template <> struct tcl_cast { static object from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; + + + + // helper for checking for required number of parameters // (throws tcl_error when not met) void check_params_no(int objc, int required, const std::string &message); @@ -128,7 +181,7 @@ class callback_base { public: virtual ~callback_base() {} - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) = 0; + virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) = 0; }; // base class for object command handlers @@ -190,138 +243,120 @@ template class class_handler : public class_handler_base { } }; -// factory functions for creating class objects -#include "cpptcl/details/constructors.h" - -// actual callback envelopes -#include "cpptcl/details/callbacks.h" - -// actual method envelopes -#include "cpptcl/details/methods.h" - -// helper meta function for figuring appropriate constructor callback -#include "cpptcl/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 struct my_index_sequence { }; +template struct my_make_index_sequence : public my_make_index_sequence { }; +template struct my_make_index_sequence<0, Is...> : public my_index_sequence { }; +template +struct fix_variadic_return { + typedef T type; +}; +template <> +struct fix_variadic_return { + typedef object type; +}; +template +struct return_dummy_value { + static TT doit(object const & o) { + return TT(); } - - 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 struct return_dummy_value { + static TT doit(object const & o) { + return o; } - - 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 +typename details::fix_variadic_return::type do_cast(bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], policies const & pol) { +static const bool is_variadic_arg = std::is_same::value && In == Ii + 1; + if (is_variadic_arg && variadic) { + return details::return_dummy_value::doit(details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol)); + } else { + return details::tcl_cast::from(interp, objv[Ii + ArgOffset], false); } +} - 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 method_v : public object_cmd_base { + //typedef R (C::*mem_type)(Ts...); + //typedef R (C::*cmem_type)(Ts...) const; + typedef Fn fn_type; + + policies policies_; + +public: + method_v(fn_type f, policies const & pol = policies()) : policies_(pol), f_(f) { } + //method_v(mem_type f, policies const & pol = policies()) : policies_(pol), f_(f), cmem_(false) { } + //method_v(cmem_type f, policies const & pol = policies()) : policies_(pol), cf_(f), cmem_(true) { } + + template struct void_return { }; + + template + void do_invoke(Fnn fn, my_index_sequence const &, C * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { + (p->*fn)(do_cast<2, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...); } - - 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 + void do_invoke(Fnn fn, my_index_sequence const &, C * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { + details::set_result(interp, (p->*fn)(do_cast<2, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...)); } - 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; + virtual void invoke(void *pv, Tcl_Interp *interp, int argc, Tcl_Obj *CONST argv[], policies const &pol__) { + check_params_no(argc, sizeof...(Ts), policies_.usage_); + C *p = static_cast(pv); + do_invoke(f_, my_make_index_sequence(), p, interp, argc, argv, policies_, void_return::value>()); +#if 0 + if (cmem_) { + do_invoke(cf_, my_make_index_sequence(), p, interp, argc, argv, policies_, void_return::value>()); + } else { + do_invoke(f_, my_make_index_sequence(), p, interp, argc, argv, policies_, void_return::value>()); + } +#endif + post_process_policies(interp, policies_, argv, true); } + private: + fn_type f_; + //cmem_type cf_; + //bool cmem_; +}; - 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; - } +} // namespace details - 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; - } +// init type for defining class constructors +//template class init {}; - 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 struct init { }; + +// no_init type and object - to define classes without constructors +namespace details { +struct no_init_type {}; - 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 callback_v : public details::callback_base { + typedef R (*functor_type)(Ts...); + functor_type f_; - 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; - } + policies policies_; +public : + callback_v(functor_type f, policies const & pol) : policies_(pol), f_(f) { } - 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 struct void_return { }; - 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 + void do_invoke(my_index_sequence const &, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { + details::set_result(interp, f_(do_cast<1, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...)); } - - 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 + void do_invoke(my_index_sequence const &, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { + f_(do_cast<1, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...); } - 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; + void invoke(Tcl_Interp * interp, int argc, Tcl_Obj * const argv []) { + check_params_no(argc - 1, sizeof...(Ts) - (policies_.variadic_ ? 1 : 0), policies_.usage_); + do_invoke(my_make_index_sequence(), interp, argc, argv, policies_, void_return::value>()); + post_process_policies(interp, policies_, argv, false); } - - 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 @@ -340,66 +375,273 @@ class interpreter { interpreter(const interpreter &i); interpreter(); - public: - interpreter(Tcl_Interp *, bool owner = false); - ~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); } + class TclChannelStreambuf : public std::streambuf { + private: + Tcl_Channel channel_; + Tcl_DString iBuffer_; + int iOffset_; + public: + TclChannelStreambuf (Tcl_Channel channel) + : channel_ (channel) + { + Tcl_DStringInit (&iBuffer_); + iOffset_ = 0; + base_ = (char *) malloc(1024); + buf_size_ = 1024; + my_base_ = true; + } + ~TclChannelStreambuf () + { + Tcl_DStringFree (&iBuffer_); + } - 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); } + char * base() { + return base_; + } + char * ebuf() { + return base_ + buf_size_; + } + char * base_; + bool my_base_; + std::streamsize buf_size_; + + std::streambuf * setbuf(char * s, std::streamsize n) { + if (my_base_) { + free(base_); + my_base_ = false; + } + base_ = s; + buf_size_ = n; + } - 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); } + void doallocate() { + } + void cdoallocate() { + } - 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); } + std::streamsize xsputn(const char_type * s, std::streamsize n) { + //std::cerr << "xsputn " << n << "\n"; + if (base() == 0) { + int r = Tcl_Write(channel_, s, n); + return r; + } + if (pptr() < ebuf()) { + std::streamsize nn = std::min(n, ebuf() - pptr()); + memcpy(pptr(), s, nn); + setp(pptr() + nn, ebuf()); + return nn; + } + return 0; + } + int overflow (int c) { + int status; + + //std::cerr << "overflow " << (char) c << "\n"; + + // Allocate reserve area if necessary + + if (base() == 0) { + char b[1]; + b[0] = c; + status = Tcl_Write (channel_, b, 1); + if (status != 1) return EOF; + return 1; + // cdoallocate (); + } + + // If there's no output buffer, allocate one. Place it after the + // end of the input buffer if there's input. + + if (!pbase ()) { + if (egptr () > gptr ()) { + setp (egptr (), ebuf ()); + } else { + setp (base (), ebuf ()); + } + } + + // If there's stuff in the output buffer, write it to the channel. + + if (pptr () > pbase ()) { + status = Tcl_Write (channel_, pbase (), pptr () - pbase ()); + if (status < (pptr () - pbase ())) { + return EOF; + } + setp (pbase (), ebuf ()); + } + + // Save the next character in the output buffer. If there is none, + // flush the channel + + if (c != EOF) { + *(pptr()) = c; + pbump (1); + return c; + } else { + setp (0, 0); + status = Tcl_Flush (channel_); + if (status != TCL_OK) return EOF; + return 0; + } + } + + int underflow () + { + + // Nothing to do if the buffer hasn't underflowed. + + if (egptr() > gptr()) { + return *gptr (); + } + + // Make sure we have a reserve area + + if (!base()) { + doallocate (); + } + + // Flush any pending output + + if (pptr () > pbase ()) { + if (overflow (EOF) == EOF) { + return EOF; + } + } + + // Get a fresh line of input if needed + + if (iOffset_ >= Tcl_DStringLength (&iBuffer_)) { + if (Tcl_Gets (channel_, &iBuffer_)) { + return EOF; + } + Tcl_DStringAppend (&iBuffer_, "\n", 1); + } + + // Determine how much input to transfer. Don't fill the reserve + // area more than half full + + size_t xferlen = Tcl_DStringLength (&iBuffer_); + if ((long) xferlen > (ebuf () - base ()) / 2) { + xferlen = (ebuf () - base ()) / 2; + } + + // Copy string into the buffer, and advance pointers + + memcpy ((void *) base (), (void *) Tcl_DStringValue (&iBuffer_), xferlen); + iOffset_ += xferlen; + setg (base (), base (), base () + xferlen); + + // Free the input string if we're finished with it + + if (iOffset_ >= Tcl_DStringLength (&iBuffer_)) { + Tcl_DStringFree (&iBuffer_); + iOffset_ = 0; + } + + // Return the first character read. + + return *gptr (); + } + + int sync () { + // Flush output + + if (overflow (EOF) == EOF) { + return EOF; + } + + // Discard input + setg (0, 0, 0); + + return 0; + } + }; +public: + std::iostream & tin() { + if (! tin_.rdbuf()) { + tin_.rdbuf(new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDIN))); + } + return tin_; + } + std::iostream & tout() { + if (! tout_.rdbuf()) { + tout_.rdbuf(new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDOUT))); + } + return tout_; + } + std::iostream & terr() { + if (! terr_.rdbuf()) { + terr_.rdbuf(new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDERR))); + terr_.rdbuf()->pubsetbuf(NULL, 0); + } + return terr_; + } - 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 class class_definer { + public: + class_definer(std::shared_ptr> ch) : ch_(ch) {} + + template + class_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { + return def2(name, fn, 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); } + template + class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies()) { + ch_->register_method(name, std::shared_ptr(new details::method_v(f, p)), p); + return *this; + } + template + class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies()) { + ch_->register_method(name, std::shared_ptr(new details::method_v(f, p)), p); + return *this; + } + private: + std::shared_ptr> ch_; + }; - // class definitions + interpreter(Tcl_Interp *, bool owner = false); + ~interpreter(); - template details::class_definer class_(std::string const &name) { - std::shared_ptr> ch(new details::class_handler()); + void make_safe(); - add_class(name, ch); + Tcl_Interp *get() const { return interp_; } - add_constructor(name, ch, std::shared_ptr(new details::callback0(&details::construct::doit))); + // free function definitions - return details::class_definer(ch); + template + void def(std::string const & name, R(*f)(Ts...), policies const & p = policies()) { + add_function(name, new details::callback_v(f, p)); } - template details::class_definer class_(std::string const &name, init const &, policies const &p = policies()) { - typedef typename details::get_callback_type_for_construct::type callback_type; + // class definitions + template struct call_constructor { + static C * doit(Ts... ts) { + return new C(ts...); + } + }; + + template class_definer class_(std::string const &name, init const & = init(), policies const &p = policies()) { + typedef details::callback_v 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::doit)), p); + add_constructor(name, ch, new callback_type(&call_constructor::doit, p), p); - return details::class_definer(ch); + return class_definer(ch); } - - template details::class_definer class_(std::string const &name, details::no_init_type const &) { + + template 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); + return class_definer(ch); } // free script evaluation @@ -434,14 +676,15 @@ class interpreter { private: void operator=(const interpreter &); - void add_function(std::string const &name, std::shared_ptr cb, policies const &p = policies()); + void add_function(std::string const &name, details::callback_base * cb); 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()); + void add_constructor(std::string const &name, std::shared_ptr chb, details::callback_base * cb, policies const &p = policies()); Tcl_Interp *interp_; bool owner_; + std::iostream tin_, tout_, terr_; }; #include "cpptcl/cpptcl_object.h" @@ -458,23 +701,22 @@ template details::result interpreter::eval(InputIterator f return details::result(interp_); } -namespace details { - -// additional callback envelopes for variadic functions -#include "cpptcl/details/callbacks_v.h" - -// additional method envelopes for variadic methods -#include "cpptcl/details/methods_v.h" - -} // namespace details - -#include "cpptcl/details/bind.h" - inline std::ostream & operator<<(std::ostream &os, const object& obj) { return os << obj.get(); } + +#if 0 + extern "C" void + TclConsoleStreambufSetup () { + cin = new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDIN)); + cout = new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDOUT)); + cerr = new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDERR)); + cerr << "C++ standard input and output now on the Tcl console" << endl; + } +#endif + } // namespace Tcl // macro for defining loadable module entry point diff --git a/cpptcl/details/bind.h b/cpptcl/details/bind.h deleted file mode 100644 index f731c4e..0000000 --- a/cpptcl/details/bind.h +++ /dev/null @@ -1,175 +0,0 @@ -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8, const T9 &t9) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - obj.append(t4); - obj.append(t5); - obj.append(t6); - obj.append(t7); - obj.append(t8); - obj.append(t9); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - obj.append(t4); - obj.append(t5); - obj.append(t6); - obj.append(t7); - obj.append(t8); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - obj.append(t4); - obj.append(t5); - obj.append(t6); - obj.append(t7); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5, const T6 &t6) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - obj.append(t4); - obj.append(t5); - obj.append(t6); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - obj.append(t4); - obj.append(t5); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - obj.append(t4); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2, const T3 &t3) { - object obj(cmd_); - obj.append(t1); - obj.append(t2); - obj.append(t3); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1, const T2 &t2) { - object obj(cmd_); - obj.append(object(t1)); - obj.append(object(t2)); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()(const T1 &t1) { - object obj(cmd_); - object e(t1); - obj.append(e); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; - -template struct Bind { - private: - object cmd_; - - public: - Bind(std::string cmd) : cmd_(object(cmd)){}; - - R operator()() { - object obj(cmd_); - return (R)(interpreter::getDefault()->eval(obj)); - } -}; diff --git a/cpptcl/details/callbacks.h b/cpptcl/details/callbacks.h deleted file mode 100644 index ac60f01..0000000 --- a/cpptcl/details/callbacks.h +++ /dev/null @@ -1,215 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -#include -#include - -template class callback0 : public callback_base { - typedef R (*functor_type)(); - - public: - callback0(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int, Tcl_Obj *CONST[], policies const &pol) { dispatch::do_dispatch(interp, f_); } - - private: - functor_type f_; -}; - -template class callback1 : public callback_base { - typedef R (*functor_type)(T1); - - public: - callback1(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 2, pol.usage_); - tcl_cast_by_reference byRef1; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value)); - } - - private: - functor_type f_; -}; - -template class callback2 : public callback_base { - typedef R (*functor_type)(T1, T2); - - public: - callback2(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 3, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value)); - } - - private: - functor_type f_; -}; - -template class callback3 : public callback_base { - typedef R (*functor_type)(T1, T2, T3); - - public: - callback3(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 4, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value)); - } - - private: - functor_type f_; -}; - -template class callback4 : public callback_base { - typedef R (*functor_type)(T1, T2, T3, T4); - - public: - callback4(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 5, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value)); - } - - private: - functor_type f_; -}; - -template class callback5 : public callback_base { - typedef R (*functor_type)(T1, T2, T3, T4, T5); - - public: - callback5(functor_type f) : f_(f) {} - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 6, pol.usage_); - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value)); - } - - private: - functor_type f_; -}; - -template class callback6 : public callback_base { - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6); - - public: - callback6(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 7, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value)); - } - - private: - functor_type f_; -}; - -template class callback7 : public callback_base { - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6, T7); - - public: - callback7(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 8, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value), tcl_cast::from(interp, objv[7], byRef7.value)); - } - - private: - functor_type f_; -}; - -template class callback8 : public callback_base { - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6, T7, T8); - - public: - callback8(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 9, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - tcl_cast_by_reference byRef8; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value), tcl_cast::from(interp, objv[7], byRef7.value), tcl_cast::from(interp, objv[8], byRef8.value)); - } - - private: - functor_type f_; -}; - -template class callback9 : public callback_base { - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6, T7, T8, T9); - - public: - callback9(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 10, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - tcl_cast_by_reference byRef8; - tcl_cast_by_reference byRef9; - - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value), tcl_cast::from(interp, objv[7], byRef7.value), tcl_cast::from(interp, objv[8], byRef8.value), - tcl_cast::from(interp, objv[9], byRef9.value)); - } - - private: - functor_type f_; -}; diff --git a/cpptcl/details/callbacks_v.h b/cpptcl/details/callbacks_v.h deleted file mode 100644 index 54d52f0..0000000 --- a/cpptcl/details/callbacks_v.h +++ /dev/null @@ -1,201 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -template class callback1 : public callback_base { - typedef object const &T1; - typedef R (*functor_type)(T1); - enum { var_start = 1 }; - - public: - callback1(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t1 = get_var_params(interp, objc, objv, var_start, pol); - dispatch::template do_dispatch(interp, f_, t1); - } - - private: - functor_type f_; -}; - -template class callback2 : public callback_base { - typedef object const &T2; - typedef R (*functor_type)(T1, T2); - enum { var_start = 2 }; - - public: - callback2(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t2 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), t2); - } - - private: - functor_type f_; -}; - -template class callback3 : public callback_base { - typedef object const &T3; - typedef R (*functor_type)(T1, T2, T3); - enum { var_start = 3 }; - - public: - callback3(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t3 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), t3); - } - - private: - functor_type f_; -}; - -template class callback4 : public callback_base { - typedef object const &T4; - typedef R (*functor_type)(T1, T2, T3, T4); - enum { var_start = 4 }; - - public: - callback4(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t4 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), t4); - } - - private: - functor_type f_; -}; - -template class callback5 : public callback_base { - typedef object const &T5; - typedef R (*functor_type)(T1, T2, T3, T4, T5); - enum { var_start = 5 }; - - public: - callback5(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t5 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), t5); - } - - private: - functor_type f_; -}; - -template class callback6 : public callback_base { - typedef object const &T6; - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6); - enum { var_start = 6 }; - - public: - callback6(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t6 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), t6); - } - - private: - functor_type f_; -}; - -template class callback7 : public callback_base { - typedef object const &T7; - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6, T7); - enum { var_start = 7 }; - - public: - callback7(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t7 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value), t7); - } - - private: - functor_type f_; -}; - -template class callback8 : public callback_base { - typedef object const &T8; - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6, T7, T8); - enum { var_start = 8 }; - - public: - callback8(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t8 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value), tcl_cast::from(interp, objv[7], byRef7.value), t8); - } - - private: - functor_type f_; -}; - -template class callback9 : public callback_base { - typedef object const &T9; - typedef R (*functor_type)(T1, T2, T3, T4, T5, T6, T7, T8, T9); - enum { var_start = 9 }; - - public: - callback9(functor_type f) : f_(f) {} - - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - object t9 = get_var_params(interp, objc, objv, var_start, pol); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - tcl_cast_by_reference byRef8; - dispatch::template do_dispatch(interp, f_, tcl_cast::from(interp, objv[1], byRef1.value), tcl_cast::from(interp, objv[2], byRef2.value), tcl_cast::from(interp, objv[3], byRef3.value), tcl_cast::from(interp, objv[4], byRef4.value), tcl_cast::from(interp, objv[5], byRef5.value), tcl_cast::from(interp, objv[6], byRef6.value), tcl_cast::from(interp, objv[7], byRef7.value), tcl_cast::from(interp, objv[8], byRef8.value), - t9); - } - - private: - functor_type f_; -}; diff --git a/cpptcl/details/constructors.h b/cpptcl/details/constructors.h deleted file mode 100644 index 7ab5110..0000000 --- a/cpptcl/details/constructors.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { return new C(t1, t2, t3, t4, t5, t6, t7, t8, t9); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { return new C(t1, t2, t3, t4, t5, t6, t7, t8); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { return new C(t1, t2, t3, t4, t5, t6, t7); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { return new C(t1, t2, t3, t4, t5, t6); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { return new C(t1, t2, t3, t4, t5); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3, T4 t4) { return new C(t1, t2, t3, t4); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2, T3 t3) { return new C(t1, t2, t3); } -}; - -template struct construct { - static C *doit(T1 t1, T2 t2) { return new C(t1, t2); } -}; - -template struct construct { - static C *doit(T1 t1) { return new C(t1); } -}; - -template struct construct { - static C *doit() { return new C(); } -}; diff --git a/cpptcl/details/conversions.h b/cpptcl/details/conversions.h deleted file mode 100644 index ff12740..0000000 --- a/cpptcl/details/conversions.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -// helper functor for converting Tcl objects to the given type -// (it is a struct instead of function, -// because I need to partially specialize it) - -template struct tcl_cast; - -template struct tcl_cast { - static T *from(Tcl_Interp *, Tcl_Obj *obj, bool byReference) { - std::string s(Tcl_GetString(obj)); - if (s.size() == 0) { - throw tcl_error("Expected pointer value, got empty string."); - } - - if (s[0] != 'p') { - throw tcl_error("Expected pointer value."); - } - - std::istringstream ss(s); - char dummy; - void *p; - ss >> dummy >> p; - - return static_cast(p); - } -}; - -// the following partial specialization is to strip reference -// (it returns a temporary object of the underlying type, which -// can be bound to the const-ref parameter of the actual function) - -template struct tcl_cast { - static T from(Tcl_Interp *interp, Tcl_Obj *obj, bool byReference) { return tcl_cast::from(interp, obj, byReference); } -}; - -template class tcl_cast_by_reference { - public: - static bool const value = false; -}; - -// the following specializations are implemented - -template <> struct tcl_cast { static int from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - -template <> struct tcl_cast { static long from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - -template <> struct tcl_cast { static bool from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - -template <> struct tcl_cast { static double from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - -template <> struct tcl_cast { static std::string from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - -template <> struct tcl_cast { static char const *from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - -template <> struct tcl_cast { static object from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - diff --git a/cpptcl/details/dispatchers.h b/cpptcl/details/dispatchers.h deleted file mode 100644 index 9a1e93b..0000000 --- a/cpptcl/details/dispatchers.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -// the dispatch class is used to execute the callback functor and to -// capture its return value -// further dispatch specialization ignores the res - -template struct dispatch { - template static void do_dispatch(Tcl_Interp *interp, Functor f) { - R res = f(); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1) { - R res = f(t1); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2) { - R res = f(t1, t2); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3) { - R res = f(t1, t2, t3); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3, T4 t4) { - R res = f(t1, t2, t3, t4); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { - R res = f(t1, t2, t3, t4, t5); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { - R res = f(t1, t2, t3, t4, t5, t6); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { - R res = f(t1, t2, t3, t4, t5, t6, t7); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { - R res = f(t1, t2, t3, t4, t5, t6, t7, t8); - set_result(interp, res); - } - - template static void do_dispatch(Tcl_Interp *interp, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { - R res = f(t1, t2, t3, t4, t5, t6, t7, t8, t9); - set_result(interp, res); - } -}; - -template <> struct dispatch { - template static void do_dispatch(Tcl_Interp *, Functor f) { f(); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1) { f(t1); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2) { f(t1, t2); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3) { f(t1, t2, t3); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3, T4 t4) { f(t1, t2, t3, t4); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { f(t1, t2, t3, t4, t5); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) { f(t1, t2, t3, t4, t5, t6); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) { f(t1, t2, t3, t4, t5, t6, t7); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) { f(t1, t2, t3, t4, t5, t6, t7, t8); } - - template static void do_dispatch(Tcl_Interp *, Functor f, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8, T9 t9) { f(t1, t2, t3, t4, t5, t6, t7, t8, t9); } -}; diff --git a/cpptcl/details/metahelpers.h b/cpptcl/details/metahelpers.h deleted file mode 100644 index d87cfa8..0000000 --- a/cpptcl/details/metahelpers.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -template struct get_callback_type_for_construct { typedef callback9 type; }; - -template struct get_callback_type_for_construct { typedef callback8 type; }; - -template struct get_callback_type_for_construct { typedef callback7 type; }; - -template struct get_callback_type_for_construct { typedef callback6 type; }; - -template struct get_callback_type_for_construct { typedef callback5 type; }; - -template struct get_callback_type_for_construct { typedef callback4 type; }; - -template struct get_callback_type_for_construct { typedef callback3 type; }; - -template struct get_callback_type_for_construct { typedef callback2 type; }; - -template struct get_callback_type_for_construct { typedef callback1 type; }; - -template struct get_callback_type_for_construct { typedef callback0 type; }; diff --git a/cpptcl/details/methods.h b/cpptcl/details/methods.h deleted file mode 100644 index 34029ff..0000000 --- a/cpptcl/details/methods.h +++ /dev/null @@ -1,304 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -template class method0 : public object_cmd_base { - typedef R (C::*mem_type)(); - typedef R (C::*cmem_type)() const; - - public: - method0(mem_type f) : f_(f), cmem_(false) {} - method0(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int, Tcl_Obj *CONST[], policies const &) { - C *p = static_cast(pv); - if (cmem_) { - dispatch::do_dispatch(interp, std::bind(cf_, p)); - } else { - dispatch::do_dispatch(interp, std::bind(f_, p)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method1 : public object_cmd_base { - typedef R (C::*mem_type)(T1); - typedef R (C::*cmem_type)(T1) const; - - public: - method1(mem_type f) : f_(f), cmem_(false) {} - method1(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 3, pol.usage_); - tcl_cast_by_reference byRef; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1), tcl_cast::from(interp, objv[2], byRef.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1), tcl_cast::from(interp, objv[2], byRef.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method2 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2); - typedef R (C::*cmem_type)(T1, T2) const; - - public: - method2(mem_type f) : f_(f), cmem_(false) {} - method2(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 4, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method3 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3); - typedef R (C::*cmem_type)(T1, T2, T3) const; - - public: - method3(mem_type f) : f_(f), cmem_(false) {} - method3(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 5, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method4 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3, T4); - typedef R (C::*cmem_type)(T1, T2, T3, T4) const; - - public: - method4(mem_type f) : f_(f), cmem_(false) {} - method4(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 6, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method5 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3, T4, T5); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5) const; - - public: - method5(mem_type f) : f_(f), cmem_(false) {} - method5(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 7, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method6 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6) const; - - public: - method6(mem_type f) : f_(f), cmem_(false) {} - method6(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 8, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method7 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6, T7); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6, T7) const; - - public: - method7(mem_type f) : f_(f), cmem_(false) {} - method7(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 9, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value), tcl_cast::from(interp, objv[8], byRef7.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value), tcl_cast::from(interp, objv[8], byRef7.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method8 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6, T7, T8); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6, T7, T8) const; - - public: - method8(mem_type f) : f_(f), cmem_(false) {} - method8(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 10, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - tcl_cast_by_reference byRef8; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value), tcl_cast::from(interp, objv[8], byRef7.value), tcl_cast::from(interp, objv[9], byRef8.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value), tcl_cast::from(interp, objv[8], byRef7.value), tcl_cast::from(interp, objv[9], byRef8.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method9 : public object_cmd_base { - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6, T7, T8, T9); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6, T7, T8, T9) const; - - public: - method9(mem_type f) : f_(f), cmem_(false) {} - method9(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - check_params_no(objc, 11, pol.usage_); - tcl_cast_by_reference byRef1; - tcl_cast_by_reference byRef2; - tcl_cast_by_reference byRef3; - tcl_cast_by_reference byRef4; - tcl_cast_by_reference byRef5; - tcl_cast_by_reference byRef6; - tcl_cast_by_reference byRef7; - tcl_cast_by_reference byRef8; - tcl_cast_by_reference byRef9; - - C *p = static_cast(pv); - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value), tcl_cast::from(interp, objv[8], byRef7.value), tcl_cast::from(interp, objv[9], byRef8.value), tcl_cast::from(interp, objv[10], byRef9.value)); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9), tcl_cast::from(interp, objv[2], byRef1.value), tcl_cast::from(interp, objv[3], byRef2.value), tcl_cast::from(interp, objv[4], byRef3.value), tcl_cast::from(interp, objv[5], byRef4.value), tcl_cast::from(interp, objv[6], byRef5.value), tcl_cast::from(interp, objv[7], byRef6.value), tcl_cast::from(interp, objv[8], byRef7.value), tcl_cast::from(interp, objv[9], byRef8.value), tcl_cast::from(interp, objv[10], byRef9.value)); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; diff --git a/cpptcl/details/methods_v.h b/cpptcl/details/methods_v.h deleted file mode 100644 index 835dea8..0000000 --- a/cpptcl/details/methods_v.h +++ /dev/null @@ -1,263 +0,0 @@ -// -// Copyright (C) 2004-2006, Maciej Sobczak -// Copyright (C) 2017-2019, FlightAware LLC -// -// 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. -// - -// Note: this file is not supposed to be a stand-alone header - -template class method1 : public object_cmd_base { - typedef object const &T1; - typedef R (C::*mem_type)(T1); - typedef R (C::*cmem_type)(T1) const; - enum { var_start = 2 }; - - public: - method1(mem_type f) : f_(f), cmem_(false) {} - method1(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t1 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1), t1); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1), t1); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method2 : public object_cmd_base { - typedef object const &T2; - typedef R (C::*mem_type)(T1, T2); - typedef R (C::*cmem_type)(T1, T2) const; - enum { var_start = 3 }; - - public: - method2(mem_type f) : f_(f), cmem_(false) {} - method2(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t2 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2), tcl_cast::from(interp, objv[2]), t2); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2), tcl_cast::from(interp, objv[2]), t2); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method3 : public object_cmd_base { - typedef object const &T3; - typedef R (C::*mem_type)(T1, T2, T3); - typedef R (C::*cmem_type)(T1, T2, T3) const; - enum { var_start = 4 }; - - public: - method3(mem_type f) : f_(f), cmem_(false) {} - method3(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t3 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), t3); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), t3); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method4 : public object_cmd_base { - typedef object const &T4; - typedef R (C::*mem_type)(T1, T2, T3, T4); - typedef R (C::*cmem_type)(T1, T2, T3, T4) const; - enum { var_start = 5 }; - - public: - method4(mem_type f) : f_(f), cmem_(false) {} - method4(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t4 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), t4); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), t4); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method5 : public object_cmd_base { - typedef object const &T5; - typedef R (C::*mem_type)(T1, T2, T3, T4, T5); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5) const; - enum { var_start = 6 }; - - public: - method5(mem_type f) : f_(f), cmem_(false) {} - method5(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t5 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), t5); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), t5); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method6 : public object_cmd_base { - typedef object const &T6; - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6) const; - enum { var_start = 7 }; - - public: - method6(mem_type f) : f_(f), cmem_(false) {} - method6(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t6 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), t6); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), t6); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method7 : public object_cmd_base { - typedef object const &T7; - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6, T7); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6, T7) const; - enum { var_start = 8 }; - - public: - method7(mem_type f) : f_(f), cmem_(false) {} - method7(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t7 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), tcl_cast::from(interp, objv[7]), t7); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), tcl_cast::from(interp, objv[7]), t7); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method8 : public object_cmd_base { - typedef object const &T8; - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6, T7, T8); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6, T7, T8) const; - enum { var_start = 9 }; - - public: - method8(mem_type f) : f_(f), cmem_(false) {} - method8(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t8 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), tcl_cast::from(interp, objv[7]), tcl_cast::from(interp, objv[8]), t8); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), tcl_cast::from(interp, objv[7]), tcl_cast::from(interp, objv[8]), t8); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; - -template class method9 : public object_cmd_base { - typedef object const &T9; - typedef R (C::*mem_type)(T1, T2, T3, T4, T5, T6, T7, T8, T9); - typedef R (C::*cmem_type)(T1, T2, T3, T4, T5, T6, T7, T8, T9) const; - enum { var_start = 10 }; - - public: - method9(mem_type f) : f_(f), cmem_(false) {} - method9(cmem_type f) : cf_(f), cmem_(true) {} - - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], policies const &pol) { - C *p = static_cast(pv); - - object t9 = get_var_params(interp, objc, objv, var_start, pol); - - if (cmem_) { - dispatch::template do_dispatch(interp, std::bind(cf_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), tcl_cast::from(interp, objv[7]), tcl_cast::from(interp, objv[8]), tcl_cast::from(interp, objv[9]), t9); - } else { - dispatch::template do_dispatch(interp, std::bind(f_, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6, std::placeholders::_7, std::placeholders::_8, std::placeholders::_9), tcl_cast::from(interp, objv[2]), tcl_cast::from(interp, objv[3]), tcl_cast::from(interp, objv[4]), tcl_cast::from(interp, objv[5]), tcl_cast::from(interp, objv[6]), tcl_cast::from(interp, objv[7]), tcl_cast::from(interp, objv[8]), tcl_cast::from(interp, objv[9]), t9); - } - } - - private: - mem_type f_; - cmem_type cf_; - bool cmem_; -}; From 818b11627888d520244a3a6bdacba272bc6264b5 Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Tue, 26 Oct 2021 00:52:28 +0200 Subject: [PATCH 02/10] more flexible argument passing --- cpptcl.cc | 378 ++++-------- cpptcl/cpptcl.h | 1258 ++++++++++++++++++++++++++++++++++------ cpptcl/cpptcl_object.h | 11 +- doc/callpolicies.md | 2 +- doc/freefun.md | 3 +- 5 files changed, 1212 insertions(+), 440 deletions(-) diff --git a/cpptcl.cc b/cpptcl.cc index 02cbb81..b74b866 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -19,88 +19,29 @@ using namespace Tcl; using namespace Tcl::details; using namespace std; - typedef std::pair, policies> callback_handler_client_data_t; struct constructor_handler_client_data_t { callback_base * cb; shared_ptr chb; - policies pol; }; - result::result(Tcl_Interp *interp) : interp_(interp) {} -result::operator bool() const { - return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); - - Tcl_Obj *obj = Tcl_GetObjResult(interp_); - - int val, cc; - cc = Tcl_GetBooleanFromObj(interp_, obj, &val); - if (cc != TCL_OK) { - throw tcl_error(interp_); - } - - return static_cast(val); -} - -result::operator double() const { - return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); - Tcl_Obj *obj = Tcl_GetObjResult(interp_); - - double val; - int cc = Tcl_GetDoubleFromObj(interp_, obj, &val); - if (cc != TCL_OK) { - throw tcl_error(interp_); - } - - return val; -} - -result::operator int() const { - return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); - Tcl_Obj *obj = Tcl_GetObjResult(interp_); - - int val, cc; - cc = Tcl_GetIntFromObj(interp_, obj, &val); - if (cc != TCL_OK) { - throw tcl_error(interp_); - } - - return val; -} - -result::operator long() const { - return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); - Tcl_Obj *obj = Tcl_GetObjResult(interp_); - - long val; - int cc; - cc = Tcl_GetLongFromObj(interp_, obj, &val); - if (cc != TCL_OK) { - throw tcl_error(interp_); - } - - return val; -} - +result::operator bool() const { return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); } +result::operator double() const { return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); } +result::operator int() const { return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); } +result::operator long() const { return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); } result::operator string() const { Tcl_Obj *obj = Tcl_GetObjResult(interp_); return Tcl_GetString(obj); } - result::operator object() const { return object(Tcl_GetObjResult(interp_)); } -void details::set_result(Tcl_Interp *interp, bool b) { Tcl_SetObjResult(interp, Tcl_NewBooleanObj(b)); } - -void details::set_result(Tcl_Interp *interp, int i) { Tcl_SetObjResult(interp, Tcl_NewIntObj(i)); } - -void details::set_result(Tcl_Interp *interp, long i) { Tcl_SetObjResult(interp, Tcl_NewLongObj(i)); } - -void details::set_result(Tcl_Interp *interp, double d) { Tcl_SetObjResult(interp, Tcl_NewDoubleObj(d)); } - +void details::set_result(Tcl_Interp *interp, bool b ) { Tcl_SetObjResult(interp, Tcl_NewBooleanObj(b)); } +void details::set_result(Tcl_Interp *interp, int i ) { Tcl_SetObjResult(interp, Tcl_NewIntObj(i)); } +void details::set_result(Tcl_Interp *interp, long i ) { Tcl_SetObjResult(interp, Tcl_NewLongObj(i)); } +void details::set_result(Tcl_Interp *interp, double d ) { Tcl_SetObjResult(interp, Tcl_NewDoubleObj(d)); } void details::set_result(Tcl_Interp *interp, string const &s) { Tcl_SetObjResult(interp, Tcl_NewStringObj(s.data(), static_cast(s.size()))); } - void details::set_result(Tcl_Interp *interp, void *p) { ostringstream ss; ss << 'p' << p; @@ -111,9 +52,16 @@ void details::set_result(Tcl_Interp *interp, void *p) { void details::set_result(Tcl_Interp *interp, object const &o) { Tcl_SetObjResult(interp, o.get_object()); } -void details::check_params_no(int objc, int required, const std::string &message) { +void details::check_params_no(int objc, int required, int maximum, const std::string &message) { if (objc < required) { - throw tcl_error(message); + std::ostringstream oss; + oss << "too few arguments: " << objc << " given, " << required << " required"; + throw tcl_error(oss.str()); + } + if (maximum != -1 && objc > maximum) { + std::ostringstream oss; + oss << "too many arguments: " << objc << " given, " << maximum << " allowed"; + throw tcl_error(oss.str()); } } @@ -121,10 +69,10 @@ object details::get_var_params(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv object o; if (pol.variadic_) { - check_params_no(objc, from, pol.usage_); + check_params_no(objc, from, -1, pol.usage_); o.assign(objv + from, objv + objc); } else { - check_params_no(objc, from + 1, pol.usage_); + check_params_no(objc, from + 1, from + 1, pol.usage_); o.assign(objv[from]); } @@ -132,7 +80,6 @@ object details::get_var_params(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv return o; } - namespace // anonymous { @@ -149,18 +96,18 @@ typedef map policies_map; policies_map call_policies; +typedef std::map all_definitions2_t; +typedef std::map all_definitions_t; +all_definitions_t all_definitions; - typedef std::map all_definitions2_t; - typedef std::map all_definitions_t; - all_definitions_t all_definitions; - // map of object handlers typedef map> class_interp_map; typedef map class_handlers_map; class_handlers_map class_handlers; -// helper for finding call policies - returns true when found +#if 0 + // helper for finding call policies - returns true when found bool find_policies(Tcl_Interp *interp, string const &cmdName, policies_interp_map::iterator &piti) { policies_map::iterator pit = call_policies.find(interp); @@ -171,10 +118,10 @@ bool find_policies(Tcl_Interp *interp, string const &cmdName, policies_interp_ma piti = pit->second.find(cmdName); return piti != pit->second.end(); } - +#endif + extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); - // actual functions handling various callbacks // generic callback handler @@ -184,7 +131,7 @@ extern "C" int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl callback_base * cb = (callback_base *) cd; try { - cb->invoke(interp, objc, objv); + cb->invoke(nullptr, interp, objc, objv); //post_process_policies(interp, cdp->second, objv, false); } catch (exception const &e) { Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); @@ -215,10 +162,10 @@ extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_O ss >> dummy >> p; try { - string methodName(Tcl_GetString(objv[1])); - policies &pol = chb->get_policies(methodName); + //string methodName(Tcl_GetString(objv[1])); + //policies &pol = chb->get_policies(methodName); - chb->invoke(p, interp, objc, objv, pol); + chb->invoke(p, interp, objc, objv); //post_process_policies(interp, pol, objv, true); } catch (exception const &e) { @@ -238,11 +185,10 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, // which is responsible for managing commands for // objects of a given type - constructor_handler_client_data_t * up = (constructor_handler_client_data_t *) cd; try { - up->cb->invoke(interp, objc, objv); + up->cb->invoke(nullptr, interp, objc, objv); // if everything went OK, the result is the address of the // new object in the 'pXXX' form @@ -262,10 +208,6 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, } // namespace - - - - Tcl::details::no_init_type Tcl::no_init; policies &policies::factory(string const &name) { @@ -278,10 +220,12 @@ policies &policies::sink(int index) { return *this; } +#if 0 policies &policies::variadic() { variadic_ = true; return *this; } +#endif policies &policies::usage(string const &message) { usage_ = std::string("Usage: ") + message; @@ -292,20 +236,21 @@ policies Tcl::factory(string const &name) { return policies().factory(name); } policies Tcl::sink(int index) { return policies().sink(index); } -policies Tcl::variadic() { return policies().variadic(); } +//policies Tcl::variadic() { return policies().variadic(); } policies Tcl::usage(string const &message) { return policies().usage(message); } class_handler_base::class_handler_base() { // default policies for the -delete command - policies_["-delete"] = policies(); + //policies_["-delete"] = policies(); } -void class_handler_base::register_method(string const &name, shared_ptr ocb, policies const &p) { +void class_handler_base::register_method(string const & name, shared_ptr ocb) { methods_[name] = ocb; - policies_[name] = p; + //policies_[name] = p; } +#if 0 policies &class_handler_base::get_policies(string const &name) { policies_map_type::iterator it = policies_.find(name); if (it == policies_.end()) { @@ -314,6 +259,7 @@ policies &class_handler_base::get_policies(string const &name) { return it->second; } +#endif object::object() : interp_(0) { obj_ = Tcl_NewObj(); @@ -325,7 +271,7 @@ object::object(bool b) : interp_(0) { Tcl_IncrRefCount(obj_); } -object::object(char const *buf, size_t size) : interp_(0) { +object::object(char const * buf, size_t size) : interp_(0) { obj_ = Tcl_NewByteArrayObj(reinterpret_cast(buf), static_cast(size)); Tcl_IncrRefCount(obj_); } @@ -345,21 +291,21 @@ object::object(long l) : interp_(0) { Tcl_IncrRefCount(obj_); } -object::object(char const *s) : interp_(0) { +object::object(char const * s) : interp_(0) { obj_ = Tcl_NewStringObj(s, -1); Tcl_IncrRefCount(obj_); } -object::object(string const &s) : interp_(0) { +object::object(string const & s) : interp_(0) { obj_ = Tcl_NewStringObj(s.data(), static_cast(s.size())); Tcl_IncrRefCount(obj_); } -object::object(Tcl_Obj *o, bool shared) : interp_(0) { init(o, shared); } +object::object(Tcl_Obj * o, bool shared) : interp_(0) { init(o, shared); } -object::object(object const &other, bool shared) : interp_(other.get_interp()) { init(other.obj_, shared); } +object::object(object const & other, bool shared) : interp_(other.get_interp()) { init(other.obj_, shared); } -void object::init(Tcl_Obj *o, bool shared) { +void object::init(Tcl_Obj * o, bool shared) { if (shared) { obj_ = o; } else { @@ -426,65 +372,23 @@ object &object::swap(object &other) { return *this; } -template <> bool object::get(interpreter &i) const { - int retVal; - int res = Tcl_GetBooleanFromObj(i.get(), obj_, &retVal); - if (res != TCL_OK) { - throw tcl_error(i.get()); - } - - return static_cast(retVal); -} - -template <> vector object::get>(interpreter &) const { - size_t size; - char const *buf = get(size); - return vector(buf, buf + size); -} - -template <> double object::get(interpreter &i) const { - double retVal; - int res = Tcl_GetDoubleFromObj(i.get(), obj_, &retVal); - if (res != TCL_OK) { - throw tcl_error(i.get()); - } - - return retVal; -} - -template <> int object::get(interpreter &i) const { - int retVal; - - int res = Tcl_GetIntFromObj(i.get(), obj_, &retVal); - if (res != TCL_OK) { - throw tcl_error(i.get()); - } - - return retVal; -} - -template <> long object::get(interpreter &i) const { - long retVal; - int res = Tcl_GetLongFromObj(i.get(), obj_, &retVal); - if (res != TCL_OK) { - throw tcl_error(i.get()); - } - - return retVal; +template T object::get(interpreter &i) const { + return tcl_cast::from(i.get(), obj_, false); } -template <> char const *object::get(interpreter &) const { return get(); } - -template <> string object::get(interpreter &) const { - int len; - char const *buf = Tcl_GetStringFromObj(obj_, &len); - return string(buf, buf + len); -} +template bool object::get(interpreter &i) const; +template double object::get(interpreter &i) const; +template float 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; string object::asString() const { return get(); } -int object::asInt() const { return get(); } -bool object::asBool() const { return get(); } -long object::asLong() const { return get(); } +int object::asInt() const { return get(); } +bool object::asBool() const { return get(); } +long object::asLong() const { return get(); } double object::asDouble() const { return get(); } char const *object::get() const { return Tcl_GetString(obj_); } @@ -496,45 +400,51 @@ char const *object::get(size_t &size) const { return const_cast(reinterpret_cast(buf)); } +bool object::is_list() const { + return obj_->typePtr && strcmp(obj_->typePtr->name, "list") == 0; +} + size_t object::size(interpreter &i) const { int len; - int res = Tcl_ListObjLength(i.get(), obj_, &len); - - if (res != TCL_OK) { + if (Tcl_ListObjLength(i.get(), obj_, &len) != TCL_OK) { throw tcl_error(i.get()); } - return static_cast(len); } -object object::at(size_t index, interpreter &i) const { +object object::at_ref(size_t index, interpreter & i) const { Tcl_Obj *o; - int res = Tcl_ListObjIndex(i.get(), obj_, static_cast(index), &o); - if (res != TCL_OK) { + if (Tcl_ListObjIndex(i.get(), obj_, static_cast(index), &o) != TCL_OK) { throw tcl_error(i.get()); } if (o == NULL) { throw tcl_error("Index out of range."); } + return object(o, true); +} +object object::at(size_t index, interpreter &i) const { + Tcl_Obj *o; + if (Tcl_ListObjIndex(i.get(), obj_, static_cast(index), &o) != TCL_OK) { + throw tcl_error(i.get()); + } + if (o == NULL) { + throw tcl_error("Index out of range."); + } return object(o); } object &object::append(object const &o, interpreter &i) { - int res = Tcl_ListObjAppendElement(i.get(), obj_, o.obj_); - if (res != TCL_OK) { + if (Tcl_ListObjAppendElement(i.get(), obj_, o.obj_) != TCL_OK) { throw tcl_error(i.get()); } - return *this; } object &object::append_list(object const &o, interpreter &i) { - int res = Tcl_ListObjAppendList(i.get(), obj_, o.obj_); - if (res != TCL_OK) { + if (Tcl_ListObjAppendList(i.get(), obj_, o.obj_) != TCL_OK) { throw tcl_error(i.get()); } - return *this; } @@ -596,14 +506,12 @@ interpreter::~interpreter() { if (owner_) { // clear all callback info belonging to this interpreter clear_definitions(interp_); - Tcl_DeleteInterp(interp_); } } void interpreter::make_safe() { - int cc = Tcl_MakeSafe(interp_); - if (cc != TCL_OK) { + if (Tcl_MakeSafe(interp_) != TCL_OK) { throw tcl_error(interp_); } } @@ -613,7 +521,6 @@ result interpreter::eval(string const &script) { if (cc != TCL_OK) { throw tcl_error(interp_); } - return result(interp_); } @@ -627,7 +534,6 @@ result interpreter::eval(object const &o) { if (cc != TCL_OK) { throw tcl_error(interp_); } - return result(interp_); } @@ -640,7 +546,6 @@ result interpreter::getVar(string const &variableName, string const &indexName) } else { Tcl_SetObjResult(interp_, obj); } - return result(interp_); } @@ -652,7 +557,6 @@ result interpreter::getVar(string const &variableName) { } else { Tcl_SetObjResult(interp_, obj); } - return result(interp_); } @@ -700,55 +604,6 @@ void interpreter::clear_definitions(Tcl_Interp *interp) { class_handlers.erase(interp); } -#if 0 -void interpreter::clear_definitions(Tcl_Interp *interp) { - // delete all callbacks that were registered for given interpreter - - { - callback_map::iterator it = callbacks.find(interp); - - if (it == callbacks.end()) { - // no callbacks defined for this interpreter - return; - } - - callback_interp_map &imap = it->second; - for (callback_interp_map::iterator it2 = imap.begin(); it2 != imap.end(); ++it2) { - Tcl_DeleteCommand(interp, it2->first.c_str()); - } - - callbacks.erase(interp); - } - - // delete all constructors - - { - callback_map::iterator it = constructors.find(interp); - if (it == constructors.end()) { - // no callbacks defined for this interpreter - return; - } - - callback_interp_map &imap = it->second; - for (callback_interp_map::iterator it2 = imap.begin(); it2 != imap.end(); ++it2) { - Tcl_DeleteCommand(interp, it2->first.c_str()); - } - - callbacks.erase(interp); - } - - // delete all call policies - - call_policies.erase(interp); - - // delete all object handlers - // (we have to assume that all living objects were destroyed, - // otherwise Bad Things will happen) - - class_handlers.erase(interp); -} -#endif - void interpreter::add_function(string const &name, callback_base * cb) { Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, cb, 0); all_definitions[interp_][name] = cb; @@ -762,37 +617,61 @@ void interpreter::add_constructor(string const &name, shared_ptrcb = cb; up->chb = chb; - up->pol = p; Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, up, 0); all_definitions[interp_][name] = cb; - - //Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, static_cast(chb.get()), 0); - - //constructors[interp_][name] = cb; - //call_policies[interp_][name] = p; } int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { int res; - int cc = Tcl_GetIntFromObj(interp, obj, &res); - if (cc != TCL_OK) { + if (Tcl_GetIntFromObj(interp, obj, &res) != TCL_OK) { throw tcl_error(interp); } - return res; } long tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { long res; - int cc = Tcl_GetLongFromObj(interp, obj, &res); - if (cc != TCL_OK) { + if (Tcl_GetLongFromObj(interp, obj, &res) != TCL_OK) { + throw tcl_error(interp); + } + return res; +} + +bool tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { + int res; + if (Tcl_GetBooleanFromObj(interp, obj, &res) != TCL_OK) { throw tcl_error(interp); } + return res != 0; +} +double tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { + double res; + if (Tcl_GetDoubleFromObj(interp, obj, &res) != TCL_OK) { + throw tcl_error(interp); + } return res; } +float tcl_cast::from(Tcl_Interp * interp, Tcl_Obj * obj, bool b) { + return tcl_cast::from(interp, obj, b); +} + +string tcl_cast::from(Tcl_Interp *, Tcl_Obj *obj, bool) { return Tcl_GetString(obj); } +char const *tcl_cast::from(Tcl_Interp *, Tcl_Obj *obj, bool) { return Tcl_GetString(obj); } + +object tcl_cast::from(Tcl_Interp * interp, Tcl_Obj * obj, bool) { + object o(obj); + o.set_interp(interp); + return o; +} + +std::vector tcl_cast >::from(Tcl_Interp * interp, Tcl_Obj * obj, bool asref) { + std::string s = tcl_cast::from(interp, obj, asref); + return std::vector(s.begin(), s.end()); +} + namespace Tcl { // helper function for post-processing call policies // for both free functions (isMethod == false) @@ -846,34 +725,3 @@ void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST obj } } } - -bool tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { - int res; - int cc = Tcl_GetBooleanFromObj(interp, obj, &res); - if (cc != TCL_OK) { - throw tcl_error(interp); - } - - return res != 0; -} - -double tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { - double res; - int cc = Tcl_GetDoubleFromObj(interp, obj, &res); - if (cc != TCL_OK) { - throw tcl_error(interp); - } - - return res; -} - -string tcl_cast::from(Tcl_Interp *, Tcl_Obj *obj, bool) { return Tcl_GetString(obj); } - -char const *tcl_cast::from(Tcl_Interp *, Tcl_Obj *obj, bool) { return Tcl_GetString(obj); } - -object tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { - object o(obj); - o.set_interp(interp); - - return o; -} diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index 35b8e66..13b2567 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -1,3 +1,4 @@ +//-*- mode: c++; eval: (c-set-offset 'innamespace 0); -*- // // Copyright (C) 2004-2006, Maciej Sobczak // Copyright (C) 2017-2019, FlightAware LLC @@ -20,6 +21,7 @@ #endif #include +#include #include #include #include @@ -29,6 +31,9 @@ #include #include #include +#include +#include +#include // // Using TCL stubs is the default behavior @@ -60,7 +65,7 @@ class tcl_error : public std::runtime_error { struct policies { policies() : variadic_(false), usage_("Too few arguments.") {} - + policies &factory(std::string const &name); // note: this is additive @@ -79,7 +84,7 @@ struct policies { // syntax short-cuts policies factory(std::string const &name); policies sink(int index); -policies variadic(); +//policies variadic(); policies usage(std::string const &message); class interpreter; @@ -88,33 +93,6 @@ class object; void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod); 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); - template struct tcl_cast; template struct tcl_cast { @@ -151,27 +129,43 @@ template class tcl_cast_by_reference { }; // the following specializations are implemented - template <> struct tcl_cast { static int from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - template <> struct tcl_cast { static long from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - template <> struct tcl_cast { static bool from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - template <> struct tcl_cast { static double from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - +template <> struct tcl_cast { static float from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; template <> struct tcl_cast { static std::string from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - template <> struct tcl_cast { static char const *from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; - template <> struct tcl_cast { static object from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; +template <> struct tcl_cast > { static std::vector from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; +// 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_; +}; + +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 for checking for required number of parameters // (throws tcl_error when not met) -void check_params_no(int objc, int required, const std::string &message); +void check_params_no(int objc, int required, int maximum, const std::string &message); // 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); @@ -181,7 +175,7 @@ class callback_base { public: virtual ~callback_base() {} - virtual void invoke(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) = 0; + virtual void invoke(void * dummyp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) = 0; }; // base class for object command handlers @@ -190,8 +184,7 @@ 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; + virtual void invoke(void *p, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) = 0; }; // base class for all class handlers, still abstract @@ -201,9 +194,9 @@ class class_handler_base : public object_cmd_base { class_handler_base(); - void register_method(std::string const &name, std::shared_ptr ocb, policies const &p); + void register_method(std::string const &name, std::shared_ptr ocb); - policies &get_policies(std::string const &name); + //policies &get_policies(std::string const &name); protected: typedef std::map > method_map_type; @@ -211,18 +204,18 @@ class class_handler_base : public object_cmd_base { // a map of methods for the given class method_map_type methods_; - policies_map_type policies_; + //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) { + virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { C *p = static_cast(pv); - if (objc < 2) { - throw tcl_error(pol.usage_); - } + //if (objc < 2) { + // throw tcl_error(pol.usage_); + //} std::string methodName(Tcl_GetString(objv[1])); @@ -239,124 +232,546 @@ template class class_handler : public class_handler_base { throw tcl_error("Method " + methodName + " not found."); } - it->second->invoke(pv, interp, objc, objv, pol); + it->second->invoke(pv, interp, objc, objv); + } +}; + +} // details + +class interpreter; + +template +struct optional { + T val; + bool valid; + operator bool() const { return valid; } + T const & operator*() const { return val; } + T const * operator->() const { return &val; } + optional() : valid(false) { } + optional(T) : valid(false) { } + optional(T t, bool v) : val(t), valid(v) { } +}; + +template +struct getopt : public optional { + using optional::optional; +}; + +template +using opt = optional; + +template +struct any; + +namespace details { +template +struct is_any { + static const bool value = false; +}; +template +struct is_any > { + static const bool value = true; +}; + +template +struct is_basic_type_impl { + static const bool value = false; +}; +template +struct is_basic_type_impl { + static const bool value = std::is_same::value || is_basic_type_impl::value; +}; + +template +struct is_basic_type { + static const bool value = is_basic_type_impl >::value; +}; + +} // details + +template +struct list { + interpreter * interp_; + Tcl_Obj * lo_; + Tcl_ObjType * ot_; + + static const bool isany = details::is_any::value; + + list() : interp_(nullptr) { } + list(interpreter * interp, Tcl_Obj * lo, Tcl_ObjType * ot) : interp_(interp), lo_(lo), ot_(ot) { } + + //list(list const & other) : ot_(other.ot_), interp_(other.interp_), lo_(other.lo_) { std::cerr << '#'; } + + typedef typename std::conditional::value, T, T *>::type return_t; + + operator bool() const { + return interp_; + } + + struct iterator { + const list * l; + int ix; + return_t operator*() const { + return l->at(ix); + } + void operator++(int) { ++ix; } + void operator++() { ++ix; } + bool operator!=(const iterator & other) { return ix != other.ix; } + }; + iterator begin() const { + return iterator{this, 0}; + } + iterator end() const { + return iterator{this, size()}; + } + + std::size_t size() const ; + return_t at(std::size_t ix) const; + return_t operator[](std::size_t ix) const { return at(ix); } +}; + +template +struct variadic { + Tcl_Obj * const * objv; + int objc; + interpreter * interp_; + + variadic() : objc(0) { } + variadic(interpreter * i, int c, Tcl_Obj * const * v) : interp_(i), objv(v), objc(c) { } + int size() const { return objc; } + T at(int ix) const; + T operator[](int ix) const { return at(ix); } + struct iterator { + variadic const * v; + int ix; + bool operator!=(const iterator & other) { return ix != other.ix; } + void operator++() { ++ix; } + T operator *() { return v->at(ix); } + }; + iterator begin() const { return iterator{ this, 0 }; } + iterator end() const { return iterator{ this, size() }; } + operator bool() const { return objc; } +}; + +template +struct overload { + +}; + +namespace details { + +template +struct is_variadic { + static const bool value = false; +}; +template +struct is_variadic > { + static const bool value = true; +}; + +template +struct has_variadic { + static const bool value = false; +}; +template +struct has_variadic : public has_variadic { }; +template +struct has_variadic, Ts...> { + static const bool value = true; +}; +template +struct has_variadic const &, Ts...> { + static const bool value = true; +}; + +template +struct remove_rc { + typedef T type; +}; +template +struct remove_rc { + typedef T type; +}; + +template +struct is_list { + static const bool value = false; +}; +template +struct is_list > { + static const bool value = true; +}; + +template +struct all_lists_impl { + static const bool value = true; +}; +template +struct all_lists_impl, Ts...> { + static const bool value = all_lists_impl::value; +}; +template +struct all_lists_impl { + static const bool value = false; +}; + +template +struct all_lists { + static const bool value = false; +}; +template +struct all_lists > : public all_lists_impl { +}; + +template +struct any_impl { + Tcl_Obj * o_; + int which_; + void set_which(interpreter *, Tcl_Obj * o) { + //throw tcl_error(std::string("type ") + (o->typePtr ? o->typePtr->name : "(none)") + " is not in any<> type list"); + which_ = -1; + } +}; +template +struct any_impl : public any_impl { + void set_which(interpreter * interp, Tcl_Obj * o); +}; + +template +struct type_at { + static const int value = -1; +}; +template +struct type_at { + static const int value = std::is_same::value ? sizeof...(Ts) : type_at::value; +}; +} + +template +struct any : public details::any_impl { + typedef details::any_impl super_t; + any() { super_t::which_ = -1; } + interpreter * interp_; + + any(interpreter * i, Tcl_Obj * o) : interp_(i) { + //this->p_ = o->internalRep.otherValuePtr; + this->o_ = o; + if (o) { + super_t::set_which(i, o); + } + } + operator bool() const { + return super_t::which_ != -1; + } + + template + void visit(Fs... f) const; + + template + typename std::conditional::value, TT, TT *>::type as() const; +}; + +template struct init { }; + +namespace details { + +template +struct is_optional { + static const bool value = false; +}; +template +struct is_optional > { + static const bool value = true; +}; +template +struct is_optional const &> { + static const bool value = true; +}; + +template +struct is_getopt { + static const bool value = false; +}; +template +struct is_getopt > { + static const bool value = true; +}; +template +struct is_getopt const &> { + static const bool value = true; +}; + +template +struct optional_unpack { + typedef T type; +}; +template +struct optional_unpack > { + typedef T type; +}; +template +struct optional_unpack > { + typedef T type; +}; + +template +struct list_unpack { + typedef T type; +}; +template +struct list_unpack > { + typedef T type; +}; + +template +struct ppack { +}; + +template +struct num_optional { + static const int value = 0; +}; +template +struct num_optional { + static const int value = num_optional::value + (is_optional::value ? 1 : 0); +}; + +template +struct num_getopt { + static const int value = 0; +}; +template +struct num_getopt { + static const int value = num_getopt::value + (is_getopt::value ? 1 : 0); +}; + +template +struct getopt_index { + static const int value = -1; +}; +template +struct getopt_index, Ts...> { + static const int value = Ix == Itarget ? Ipos : getopt_index::value; +}; +template +struct getopt_index const &, Ts...> { + static const int value = Ix == Itarget ? Ipos : getopt_index::value; +}; +template +struct getopt_index { + static const int value = getopt_index::value; +}; + +template +struct visit_impl { + template + static void invoke(any const &) { + } +}; + +class no_argument_match { }; +template +struct argument_type { + typedef no_argument_match type; +}; +template +struct argument_type { + typedef typename std::conditional::type, T>::type * + >::value, T, typename argument_type::type>::type type; +}; + +template +struct visit_impl { + template + static void invoke(any const & a, F f, Fs... fs) { + typedef typename argument_type::type arg_t; + typedef typename argument_type::type arg_list_t; + + if constexpr (!std::is_same::value && !std::is_same::value) { + if (arg_list_t li = a.template as()) { + for (auto && i : li) { + f(i); + } + return; + } + } else if constexpr (!std::is_same::value) { + if (arg_t * p = a.template as()) { + f(p); + return; + } + } + visit_impl::invoke(a, fs...); + } +}; +} + +template +template +void any::visit(Fs... f) const { + details::visit_impl::invoke(*this, f...); +} + +namespace details { +template +struct generate_hasarg { + static void invoke(bool * arr, std::string *, const char *) { } +}; +template +struct generate_hasarg { + static void invoke(bool * arr, std::string * opts, const char * p) { + if (!is_getopt::value) { + generate_hasarg::invoke(arr, opts, p); + } else { + arr[I] = !std::is_same::type, getopt >::value; + while (*p && isspace(*p)) ++p; + if (p) { + const char * pend = p; + while (*pend && !isspace(*pend)) ++pend; + opts[I] = std::string(p, pend - p); + + generate_hasarg::invoke(arr, opts, pend); + } else { + std::cerr << "option error\n"; + } + } } }; -template struct my_index_sequence { }; -template struct my_make_index_sequence : public my_make_index_sequence { }; -template struct my_make_index_sequence<0, Is...> : public my_index_sequence { }; template struct fix_variadic_return { - typedef T type; + typedef typename std::decay::type type; }; + template <> struct fix_variadic_return { typedef object type; }; template struct return_dummy_value { - static TT doit(object const & o) { - return TT(); + static typename std::decay::type doit(object const &) { + return typename std::decay::type(); + } + template + static typename std::decay::type doit(OptionalT, bool) { + return typename std::decay::type(); + } + static typename std::decay::type doit(Tcl_Interp *, Tcl_Obj *, Tcl_ObjType *) { + return typename std::decay::type(); } }; template struct return_dummy_value { static TT doit(object const & o) { return o; } -}; -template -typename details::fix_variadic_return::type do_cast(bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], policies const & pol) { -static const bool is_variadic_arg = std::is_same::value && In == Ii + 1; - if (is_variadic_arg && variadic) { - return details::return_dummy_value::doit(details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol)); - } else { - return details::tcl_cast::from(interp, objv[Ii + ArgOffset], false); + template + static TT doit(OptionalT o, bool v) { + return TT(o, v); } -} + static TT doit(interpreter * i, Tcl_Obj * o, Tcl_ObjType *ot) { + return TT(i, o, ot); + } +}; +template +TT do_cast(ppack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol); -template class method_v : public object_cmd_base { - //typedef R (C::*mem_type)(Ts...); - //typedef R (C::*cmem_type)(Ts...) const; - typedef Fn fn_type; - - policies policies_; +struct no_init_type {}; +struct no_class { }; -public: - method_v(fn_type f, policies const & pol = policies()) : policies_(pol), f_(f) { } - //method_v(mem_type f, policies const & pol = policies()) : policies_(pol), f_(f), cmem_(false) { } - //method_v(cmem_type f, policies const & pol = policies()) : policies_(pol), cf_(f), cmem_(true) { } +template +struct class_lambda { }; - template struct void_return { }; - - template - void do_invoke(Fnn fn, my_index_sequence const &, C * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { - (p->*fn)(do_cast<2, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...); - } - template - void do_invoke(Fnn fn, my_index_sequence const &, C * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { - details::set_result(interp, (p->*fn)(do_cast<2, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...)); - } +template +struct class_lambda_unpack { + typedef C type; +}; +template +struct class_lambda_unpack > { + typedef C type; +}; - virtual void invoke(void *pv, Tcl_Interp *interp, int argc, Tcl_Obj *CONST argv[], policies const &pol__) { - check_params_no(argc, sizeof...(Ts), policies_.usage_); - C *p = static_cast(pv); - do_invoke(f_, my_make_index_sequence(), p, interp, argc, argv, policies_, void_return::value>()); -#if 0 - if (cmem_) { - do_invoke(cf_, my_make_index_sequence(), p, interp, argc, argv, policies_, void_return::value>()); - } else { - do_invoke(f_, my_make_index_sequence(), p, interp, argc, argv, policies_, void_return::value>()); - } -#endif - post_process_policies(interp, policies_, argv, true); - } - private: - fn_type f_; - //cmem_type cf_; - //bool cmem_; +template +struct is_class_lambda { + static const bool value = !std::is_same::type>::value; }; -} // namespace details +template class callback_v : public std::conditional::value, details::callback_base, details::object_cmd_base>::type { + typedef typename class_lambda_unpack::type C; -// init type for defining class constructors -//template class init {}; + typedef Fn functor_type; + policies policies_; + functor_type f_; -template struct init { }; - -// no_init type and object - to define classes without constructors -namespace details { -struct no_init_type {}; + static const int num_opt = num_getopt::value; + std::string opts_[num_opt]; -template class callback_v : public details::callback_base { - typedef R (*functor_type)(Ts...); - functor_type f_; + bool has_arg_[num_opt + 1] = { false }; - policies policies_; -public : - callback_v(functor_type f, policies const & pol) : policies_(pol), f_(f) { } + static const int arg_offset = -num_opt; + interpreter * interpreter_; + public : + callback_v(interpreter * i, functor_type f, policies const & pol, const char * opts = nullptr) : interpreter_(i), policies_(pol), f_(f) { + if (num_opt) { + if (! opts) { + throw tcl_error("no getopt string supplied for function taking getopt<> arguments"); + } + generate_hasarg<0, Ts...>::invoke(has_arg_, opts_, opts); + } + } template struct void_return { }; - template - void do_invoke(my_index_sequence const &, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { - details::set_result(interp, f_(do_cast<1, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...)); + // regular function (return / void) + template ::value, bool>::type = true> + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) { + if (argc && argv) { } + details::set_result(interp, f_(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); + } + template ::value, bool>::type = true> + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { + if (argc && argv && interp) { } + f_(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); + } + + + // method implemented by a lambda (return / void) + template ::value, bool>::type = true> + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { + if (argc && argv) { } + details::set_result(interp, f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); } - template - void do_invoke(my_index_sequence const &, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], policies const & pol, void_return) { - f_(do_cast<1, Ts, sizeof...(Is), Is>(pol.variadic_, interp, argc, argv, pol)...); + template ::value, bool>::type = true> + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { + if (argc && argv && interp) { } + f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); } + - void invoke(Tcl_Interp * interp, int argc, Tcl_Obj * const argv []) { - check_params_no(argc - 1, sizeof...(Ts) - (policies_.variadic_ ? 1 : 0), policies_.usage_); - do_invoke(my_make_index_sequence(), interp, argc, argv, policies_, void_return::value>()); - post_process_policies(interp, policies_, argv, false); + // method (return / void) + template ::value && !std::is_same::value, bool>::type = true> + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { + if (argc && argv) { } + details::set_result(interp, (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); } + template ::value && !std::is_same::value, bool>::type = true> + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { + if (argc && argv && interp) { } + (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); + } + + void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv []); }; +template +struct std_function { + typedef void type; +}; +template +struct std_function { + typedef std::function type; +}; } // namespace details - extern details::no_init_type no_init; // interpreter wrapper @@ -370,7 +785,6 @@ class interpreter { } return defaultInterpreter; } - private: interpreter(const interpreter &i); interpreter(); @@ -412,16 +826,10 @@ class interpreter { } base_ = s; buf_size_ = n; + return this; } - void doallocate() { - } - void cdoallocate() { - } - - std::streamsize xsputn(const char_type * s, std::streamsize n) { - //std::cerr << "xsputn " << n << "\n"; if (base() == 0) { int r = Tcl_Write(channel_, s, n); return r; @@ -447,7 +855,6 @@ class interpreter { status = Tcl_Write (channel_, b, 1); if (status != 1) return EOF; return 1; - // cdoallocate (); } // If there's no output buffer, allocate one. Place it after the @@ -488,21 +895,10 @@ class interpreter { int underflow () { - - // Nothing to do if the buffer hasn't underflowed. - if (egptr() > gptr()) { return *gptr (); } - // Make sure we have a reserve area - - if (!base()) { - doallocate (); - } - - // Flush any pending output - if (pptr () > pbase ()) { if (overflow (EOF) == EOF) { return EOF; @@ -578,64 +974,260 @@ class interpreter { return terr_; } + void trace(bool v) { + trace_ = v; + } + bool trace() const { + return trace_; + } + bool trace_ = false; template class class_definer { public: - class_definer(std::shared_ptr> ch) : ch_(ch) {} + class_definer(std::shared_ptr> ch, interpreter * inter = nullptr) : ch_(ch), interp_(inter) {} template - class_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { - return def2(name, fn, p); + class_definer & def(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { + return def2(name, fn, p, opts); } template - class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::method_v(f, p)), p); + class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies(), const char * opts = nullptr) { + ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p, opts))); return *this; } template - class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::method_v(f, p)), p); + class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies(), const char * opts = nullptr) { + ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p, opts))); + return *this; + } + template + class_definer & def2(std::string const & name, std::function fn, policies const & p = policies(), const char * opts = nullptr) { + ch_->register_method(name, std::shared_ptr(new details::callback_v, std::function, R, Ts...>(interp_, fn, p, opts))); return *this; } + + template + class_definer & def2(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { + return def2(name, typename details::std_function::type(fn), p, opts); + } + + template + class_definer & defvar(std::string const & name, T C::*varp); private: + interpreter * interp_; std::shared_ptr> ch_; }; + template + struct function_definer { + function_definer(C * this_p, interpreter * interp) : this_p_(this_p), interp_(interp) { } + + template + function_definer & def(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { + interp_->def(name, fn, this_p_, p, opts); + return *this; + } + + template + function_definer & defvar(std::string const & name, T C::*t); + + C * this_p_; + interpreter * interp_; + }; + interpreter(Tcl_Interp *, bool owner = false); ~interpreter(); void make_safe(); Tcl_Interp *get() const { return interp_; } - + Tcl_Interp * get_interp() { return interp_; } + // free function definitions template - void def(std::string const & name, R(*f)(Ts...), policies const & p = policies()) { - add_function(name, new details::callback_v(f, p)); + void def(std::string const & name, R(*f)(Ts...), policies const & p = policies(), const char * opts = nullptr) { + add_function(name, new details::callback_v(this, f, p, opts)); + } + template + void def(std::string const & name, std::function fn, policies const & p = policies(), const char * opts = nullptr) { + add_function(name, new details::callback_v, R, Ts...>(this, fn, p, opts)); } - // class definitions + template + void def(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { + def(name, typename details::std_function::type(fn), p, opts); + } + + template + void def(std::string const & name, R (C::*f)(Ts...), C * this_p, policies const & p = policies(), const char * opts = nullptr) { + def(name, [this_p, f](Ts... args) { return (this_p->*f)(args...); }, p, opts); + } + + template + void defvar(std::string const & name, T & v); + + template + function_definer def(C * this_p) { + return function_definer(this_p, this); + } template struct call_constructor { static C * doit(Ts... ts) { return new C(ts...); } }; - + template class_definer class_(std::string const &name, init const & = init(), policies const &p = policies()) { - typedef details::callback_v callback_type; + typedef details::callback_v callback_type; std::shared_ptr> ch(new details::class_handler()); add_class(name, ch); - add_constructor(name, ch, new callback_type(&call_constructor::doit, p), p); + add_constructor(name, ch, new callback_type(this, &call_constructor::doit, p), p); return class_definer(ch); } + + std::map obj_type_by_tclname_; + std::map obj_type_by_cppname_; + + struct type_ops { + static void dup(Tcl_Obj * src, Tcl_Obj * dst) { + dst->internalRep.otherValuePtr = src->internalRep.otherValuePtr; + dst->typePtr = src->typePtr; + //std::cerr << "dup osrc=" << src << " odst=" << dst << " type=" << src->typePtr << " intern=" << src->internalRep.otherValuePtr << "\n"; + } + static void free(Tcl_Obj *) { + } + static void str(Tcl_Obj *) { + } + static void str_impl(Tcl_Obj * o, std::string const & name) { + o->bytes = Tcl_Alloc(name.size() + 1); + strncpy(o->bytes, name.c_str(), name.size()); + o->bytes[name.size()] = 0; + o->length = name.size(); + } + static int set(Tcl_Interp *, Tcl_Obj *) { + return TCL_OK; + } + }; + + template + Tcl_ObjType * get_objtype() { + auto it = obj_type_by_cppname_.find(std::type_index(typeid(TY))); + if (it != obj_type_by_cppname_.end()) { + return it->second; + } + return nullptr; + } + Tcl_ObjType * get_objtype(const char * name) { + auto & o = obj_type_by_tclname_; + auto it = o.find(name); + return it == o.end() ? nullptr : it->second; + } + template + bool is_type(Tcl_Obj * o) { + return o->typePtr && get_objtype() == o->typePtr; + if (o->typePtr) { + std::type_index * tip = (std::type_index *) (o->typePtr->name + 256); + if (std::type_index(typeid(T)) == *tip) { + return true; + } + } + return false; + } + template + T * try_type(Tcl_Obj * o) { + if (is_type(o)) { + return (T *) o->internalRep.otherValuePtr; + } + return nullptr; + } + template + void type(std::string const & name, TY * p) { + auto it = obj_type_by_cppname_.find(std::type_index(typeid(TY))); + if (it != obj_type_by_cppname_.end()) { + std::cerr << "attempt to register type " << name << " twice\n"; + return; + } + auto it2 = obj_type_by_tclname_.find(name); + if (it2 != obj_type_by_tclname_.end()) { + std::cerr << "attempt to register type " << name << " twice\n"; + } + Tcl_ObjType * ot = new Tcl_ObjType; + //char * cp = new char[name.size() + 1 + sizeof(std::type_index)]; + char * cp = new char[256 + sizeof(std::type_index)]; + strcpy(cp, name.c_str()); + cp[name.size()] = 0; + std::type_index * tip = (std::type_index *) (cp + 256); + *tip = std::type_index(typeid(TY)); + ot->name = cp; + ot->freeIntRepProc = &OPS::free; + ot->dupIntRepProc = &OPS::dup; + ot->updateStringProc = &OPS::str; + ot->setFromAnyProc = &OPS::set; + obj_type_by_tclname_.insert(std::pair(name, ot)); + obj_type_by_cppname_.insert(std::pair(std::type_index(typeid(TY)), ot)); + Tcl_RegisterObjType(ot); + } + + template ::type>::value, bool>::type = true> + T tcl_cast(Tcl_Interp *, Tcl_Obj *, bool) { + throw; + } + template + T tcl_cast(Tcl_Obj * o) { + return this->template tcl_cast(this->get_interp(), o, false); + } + + template ::value, bool>::type = true> + T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { + if (std::is_pointer::value && o->typePtr) { + auto it = obj_type_by_cppname_.find(std::type_index(typeid(typename std::remove_pointer::type))); + if (it != obj_type_by_cppname_.end()) { + if (it->second == o->typePtr) { + //return (typename std::conditional::value, T, T*>::type) o->internalRep.otherValuePtr; + //std::cerr << "objcast " << o << " " << o->internalRep.otherValuePtr << "\n"; + return (T) o->internalRep.otherValuePtr; + } else if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { + int len; + if (Tcl_ListObjLength(i, o, &len) == TCL_OK) { + if (len == 1) { + Tcl_Obj *o2; + if (Tcl_ListObjIndex(i, o, 0, &o2) == TCL_OK) { + if (it->second == o2->typePtr) { + //std::cerr << "objcast(list=len1) " << o2 << " " << o2->internalRep.otherValuePtr << "\n"; + return (T) o2->internalRep.otherValuePtr; + } + } + } else { + throw tcl_error("Expected single object, got list"); + } + } else { + throw tcl_error("Unknown tcl error"); + } + } else { + throw tcl_error("function argument has wrong type"); + } + } else { + throw tcl_error("function argument type is not registered"); + } + } else { + throw tcl_error("function argument is not a tcl object type"); + } + return nullptr; + return details::tcl_cast::from(i, o, byref); + //return nullptr; + } + template ::value && std::is_same::type>::value && !details::is_any::value, bool>::type = true> + T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { + return details::tcl_cast::from(i, o, byref); + } + template class_definer class_(std::string const &name, details::no_init_type const &) { std::shared_ptr> ch(new details::class_handler()); @@ -653,6 +1245,9 @@ class interpreter { // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result eval(InputIterator first, InputIterator last); + template + void setVar(std::string const & name, T const & value); + // Get a variable from TCL interpreter with Tcl_GetVar details::result getVar(std::string const &scalarTclVariable); details::result getVar(std::string const &arrayTclVariable, std::string const &arrayIndex); @@ -687,8 +1282,340 @@ class interpreter { std::iostream tin_, tout_, terr_; }; +template +std::size_t list::size() const { + if (! interp_) return 0; + int len; + if (Tcl_ListObjLength(interp_->get(), lo_, &len) == TCL_OK) { + return len; + } + return 0; +} + +template +T variadic::at(int ix) const { return interp_->template tcl_cast(objv[ix]); } + +template +typename list::return_t list::at(std::size_t ix) const { + Tcl_Obj *o; + if (Tcl_ListObjIndex(interp_->get(), lo_, ix, &o) == TCL_OK) { + if constexpr (isany) { + return return_t(interp_, o); + } else if constexpr (details::is_basic_type::value) { + return interp_->template tcl_cast(o); + } else { + if (o->typePtr == ot_) { + return (return_t) o->internalRep.otherValuePtr; + } else { + throw tcl_error("Unexpected type in list"); + } + } + } + return return_t(); +} + +template +template +typename std::conditional::value, TT, TT *>::type any::as() const { + //if (this->interp_->try_type(this->o_->typePtr)) { + if (this->which_ == details::type_at::value) { + if constexpr (details::is_list::value) { + if (this->o_->typePtr && strcmp(this->o_->typePtr->name, "list") == 0) { + return TT(interp_, this->o_, interp_->get_objtype::type>()); + } else { + return TT(); + //throw tcl_error(std::string("bad cast of ") + (this->o_->typePtr ? this->o_->typePtr->name : "(none)") + " to list"); + } + } else { + return (TT *) this->o_->internalRep.otherValuePtr; //o_->internalRep.otherValuePtr; + } + } + if constexpr (details::is_list::value) { + return TT(); + } else { + return nullptr; + } +} + +namespace details { +template +inline void any_impl::set_which(interpreter * i, Tcl_Obj * o) { + if constexpr (is_list::value) { + if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { + Tcl_Obj *oo; + if (Tcl_ListObjIndex(i->get(), o, 0, &oo) == TCL_OK) { + if (i->is_type::type>(oo)) { + this->which_ = sizeof...(Ts); + return; + } + } + } else { + if (i->is_type::type>(o)) { + this->which_ = sizeof...(Ts); + return; + } + } + any_impl::set_which(i, o); + } else { + if (i->is_type(o)) { + this->which_ = sizeof...(Ts); + } else { + any_impl::set_which(i, o); + } + } +} + +template +typename details::fix_variadic_return::type do_cast(interpreter * tcli, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol) { + typedef typename remove_rc::type TT; + + static const bool is_variadic_arg = is_variadic::value; + static const bool is_optional_arg = is_optional::value; + static const bool is_getopt_arg = is_getopt::value; + + typedef typename list_unpack::type list_unpack_t; + static const bool is_list_arg = !std::is_same::value; + static const bool cpptcl_byref = false; + + static const bool is_any_arg = is_any::value; + static const bool is_any_list_arg = is_any_arg && all_lists::value; + + typedef typename optional_unpack::type opt_unpack_t; + + //std::cerr << "do_cast " << Ii << " " << ArgOffset << " " << objc << "\n"; + if constexpr (is_optional_arg || is_getopt_arg) { + if constexpr (is_getopt_arg) { + static const int getopti = getopt_index<0, Ii, 0, Ts...>::value; + //std::cerr << "getopti " << getopti << " " << getoptv[getopti] << "\n"; + + if (getoptv[getopti]) { + if constexpr (std::is_same >::value) { + //std::cerr << "truebool\n"; + return TT(true, true); + } else { + //std::cerr << "optional\n"; + return TT(do_cast<0, opt_unpack_t, In, Ii>(tcli, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol), true); + //return TT(tcli->template tcl_cast(interp, getoptv[getopti], cpptcl_byref), true); + } + } else { + //std::cerr << "falsebool\n"; + return TT(opt_unpack_t(), false); + } + } else { + if (objc > Ii + ArgOffset) { + return TT(do_cast(tcli, pack, variadic, interp, objc, objv, getoptc, getoptv, pol), true); + //return { tcli->template tcl_cast(interp, objv[Ii + ArgOffset], cpptcl_byref), true }; + } else { + return { typename std::decay::type(), false }; + } + } + } else if constexpr (is_list_arg) { + if (objv[Ii + ArgOffset]->typePtr) { + Tcl_ObjType * ot = tcli->get_objtype(); + if (strcmp(objv[Ii + ArgOffset]->typePtr->name, "list") == 0) { + return TT(tcli, objv[Ii + ArgOffset], ot); + return return_dummy_value::doit(tcli, objv[Ii + ArgOffset], ot); + } else { + Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); + //std::cerr << "create listone " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << (ot ? ot->name : "") << "\n"; + return TT(tcli, lo, ot); + return return_dummy_value::doit(tcli, lo, ot); + } + } else { + return TT(nullptr, nullptr, nullptr); + //throw tcl_error("expecting object at index" + std::to_string(Ii + ArgOffset)); + } + } else if constexpr (is_any_arg) { + if (objv[Ii + ArgOffset]->typePtr) { + if (strcmp(objv[Ii + ArgOffset]->typePtr->name, "list") == 0) { + if constexpr (is_any_list_arg) { + int len; + if (Tcl_ListObjLength(interp, objv[Ii + ArgOffset], &len) == TCL_OK) { + if (len == 0) { + return TT(nullptr, nullptr); + } else { + return TT(tcli, objv[Ii + ArgOffset]); + } + } else { + throw tcl_error("cannot query list length"); + } + } else { + int len; + if (Tcl_ListObjLength(interp, objv[Ii + ArgOffset], &len) == TCL_OK) { + if (len == 1) { + Tcl_Obj * oo; + if (Tcl_ListObjIndex(interp, objv[Ii + ArgOffset], 0, &oo) == TCL_OK) { + return TT(tcli, oo); + } else throw tcl_error("cannot get list element"); + } else throw tcl_error("expecting object or list of size one, got list of size " + std::to_string(len)); + } else throw tcl_error("cannot get list size"); + throw tcl_error("cannot convert list"); + } + } else { + if (is_any_list_arg) { + Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); + return TT(tcli, lo); + } else { + return TT(tcli, objv[Ii + ArgOffset]); + } + } + //return return_dummy_value::doit(ret.set(tcli, objv[Ii + ArgOffset])); + } else { + if constexpr (is_any_list_arg) { + return TT(nullptr, nullptr); + } else { + throw tcl_error("any<> argument does not map to an object"); + } + //return return_dummy_value::doit(interp, nullptr); + //return TT(nullptr, nullptr); + } + } else { + if constexpr (is_variadic_arg) { // && variadic) { + //return details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol); + //return details::return_dummy_value::doit(details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol)); + return TT(tcli, objc, objv); + } else { + //} else { + //return details::tcl_cast::from(interp, objv[Ii + ArgOffset], cpptcl_byref); + //} + //std::cerr << "regular arg " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << objv[Ii + ArgOffset] << " " << Ii << " " << ArgOffset << " " << objv[Ii + ArgOffset]->typePtr << "[" << (objv[Ii + ArgOffset]->typePtr ? objv[Ii + ArgOffset]->typePtr->name : "") << "]\n"; + //return details::tcl_cast::from(interp, objv[Ii + ArgOffset], false); + return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false); + } + } +} + +template +void callback_v::invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv []) { + static const int num_gopt = num_getopt::value; + static const int num_opt = num_optional::value; + Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; + Tcl_Obj boolean_dummy_object; + std::size_t ai = std::is_same::value ? 1 : 2; + static const bool has_variadic_ = has_variadic::value; + + if (interpreter_->trace()) { + for (int i = 0; i < argc; ++i) { + std::cerr << " "; + if (argv[i]->typePtr && strcmp(argv[i]->typePtr->name, "list") == 0) { + std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; + } else { + std::cerr << Tcl_GetString(argv[i]); + } + } + std::cerr << "\n"; + } + + if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0) { + if (num_gopt) { + std::cerr << Tcl_GetString(argv[0]); + for (int oi = 0; oi < num_gopt; ++oi) { + interpreter_->terr() << " -" << opts_[oi] << (has_arg_[oi] ? " " : ""); + } + + if (policies_.variadic_) { + interpreter_->terr() << " objects..."; + } + interpreter_->terr() << "\n"; + } + return; + } + + if (num_gopt) { + for (; ai < argc; ++ai) { + char * s = Tcl_GetString(argv[ai]); + //std::cerr << "look at: " << s << "\n"; + if (s[0] == '-') { + if (s[1] == '-' && s[2] == 0) { + ++ai; + break; + } + bool found = false; + for (int oi = 0; oi < num_gopt; ++oi) { + if (opts_[oi].compare(s + 1) == 0) { + found = true; + //std::cerr << s << " at " << ai << " mapped to " << oi << " has_arg " << has_arg_[oi] << "\n"; + if (has_arg_[oi]) { + ++ai; + if (ai >= argc) { + throw tcl_error(std::string("option requires an argument: ") + s); + } + getopt_argv[oi] = argv[ai]; + } else { + getopt_argv[oi] = &boolean_dummy_object; + } + } + } + if (!found) { + throw tcl_error(std::string("unknown option: ") + s); + } + } else { + break; + } + } + } + check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ ? 1 : 0) - num_gopt - num_opt, has_variadic_ ? -1 : sizeof...(Ts) - num_gopt, policies_.usage_); + do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + post_process_policies(interp, policies_, argv + ai, false); +} +} + #include "cpptcl/cpptcl_object.h" +template +void interpreter::defvar(std::string const & name, T & v) { + def(name, [&v] (opt const & arg) -> T { + if (arg) { + v = *arg; + } + return v; + }); +} + +template +template +interpreter::function_definer & interpreter::function_definer::defvar(std::string const & name, T C::*t) { + return def(name, [t] (opt const & arg) -> T { + if (arg) { + *t = arg; + } + return *t; + }); +} + +template +template +interpreter::class_definer & interpreter::class_definer::defvar(std::string const & name, T C::*varp) { + return this->def(name, [varp] (C * this_p, opt const & val) -> T { + if (val) this_p->*varp = *val; + return this_p->*varp; + }); +} + +template +void interpreter::setVar(std::string const & name, T const & value) { + object val_obj(value); + object name_obj(name); + Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, val_obj.get_object(), 0); +} + +template +struct Bind { + object cmd_; + + template + void dummy(TTs...) { + } + + Bind(std::string const & cmd) : cmd_(object(cmd)) { } + R operator()(Ts... args) { + object e(cmd_); + + dummy(e.append(object(args))...); + return (R) (interpreter::getDefault()->eval(e)); + } +}; + // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result interpreter::eval(InputIterator first, InputIterator last) { std::vector v; @@ -706,17 +1633,6 @@ inline std::ostream & operator<<(std::ostream &os, const object& obj) return os << obj.get(); } - -#if 0 - extern "C" void - TclConsoleStreambufSetup () { - cin = new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDIN)); - cout = new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDOUT)); - cerr = new TclChannelStreambuf (Tcl_GetStdChannel (TCL_STDERR)); - cerr << "C++ standard input and output now on the Tcl console" << endl; - } -#endif - } // namespace Tcl // macro for defining loadable module entry point diff --git a/cpptcl/cpptcl_object.h b/cpptcl/cpptcl_object.h index 2d8cb18..582f6f3 100644 --- a/cpptcl/cpptcl_object.h +++ b/cpptcl/cpptcl_object.h @@ -138,6 +138,7 @@ class object { // (logically) non-modifying members + //template T get(interpreter &i = *interpreter::defaultInterpreter) const; template T get(interpreter &i = *interpreter::defaultInterpreter) const; char const *get() const; // string get @@ -145,7 +146,10 @@ class object { size_t size(interpreter &i = *interpreter::defaultInterpreter) const; // returns list length object at(size_t index, interpreter &i = *interpreter::defaultInterpreter) const; + object at_ref(size_t index, interpreter &i = *interpreter::defaultInterpreter) const; + bool is_list() const; + Tcl_Obj *get_object() const { return obj_; } // modifying members @@ -188,7 +192,7 @@ class object { return maybe_object(o, interp_, std::string(name), idx); } - const bool exists(std::string idx) const { + bool exists(std::string idx) const { Tcl_Obj *array = obj_; Tcl_Obj *o = Tcl_GetVar2Ex(interp_, Tcl_GetString(array), idx.c_str(), 0); return (o != 0); @@ -234,7 +238,9 @@ class object { Tcl_Interp *interp_; }; + // available specializations for object::get +#if 0 template <> bool object::get(interpreter &i) const; template <> double object::get(interpreter &i) const; template <> int object::get(interpreter &i) const; @@ -242,5 +248,8 @@ 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; +#endif + + #endif /* CPPTCL_OBJECT_H */ diff --git a/doc/callpolicies.md b/doc/callpolicies.md index 24dbbd1..7ed4c58 100644 --- a/doc/callpolicies.md +++ b/doc/callpolicies.md @@ -1,4 +1,4 @@ -[[prev](objects.md)][[top](README.md)][[next](threads.md)] +[[prev](objects.md)][[top](README.md)][[next](arguments.md)] #### Call Policies diff --git a/doc/freefun.md b/doc/freefun.md index b36e5a3..e95f640 100644 --- a/doc/freefun.md +++ b/doc/freefun.md @@ -54,8 +54,6 @@ If you create a Tcl command that's the same name as a command that already exist You probably noticed that the exposed functions can have parameters and can return values. -Functions with up to 9 parameters can be exposed. - At the moment, parameters and the return value of exposed functions can have the following types: * std::string, char const * @@ -63,6 +61,7 @@ At the moment, parameters and the return value of exposed functions can have the * int, * long, * bool, +* float, * double, * pointer to arbitrary type * [object](objects.md) From c12ce194220a58f3aa566f31d4c4390f0ed3b948 Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Thu, 28 Oct 2021 02:18:32 +0200 Subject: [PATCH 03/10] remove obsolete files from CMake --- CMakeLists.txt | 9 --------- 1 file changed, 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2741e5..a10c2f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,15 +68,6 @@ list(APPEND SRCS ${CMAKE_BINARY_DIR}/generated/cpptcl_version.cpp) list(APPEND HDRS ${cpptcl_SOURCE_DIR}/cpptcl/cpptcl.h) list(APPEND HDRS ${cpptcl_SOURCE_DIR}/cpptcl/cpptcl_object.h) list(APPEND HDRS ${cpptcl_SOURCE_DIR}/cpptcl/version.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/callbacks.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/callbacks_v.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/constructors.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/conversions.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/dispatchers.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/metahelpers.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/methods.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/methods_v.h) -list(APPEND HDRS_DETAILS ${cpptcl_SOURCE_DIR}/cpptcl/details/bind.h) add_library(cpptcl SHARED ${SRCS} ${HDRS} ${HDRS_DETAILS}) add_library(cpptcl::cpptcl ALIAS cpptcl) From 308f67ff5174ebf61797db8fa941e7bfd6c20005 Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Thu, 28 Oct 2021 02:20:56 +0200 Subject: [PATCH 04/10] forgot to add a file --- doc/arguments.md | 159 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 doc/arguments.md diff --git a/doc/arguments.md b/doc/arguments.md new file mode 100644 index 0000000..2d62547 --- /dev/null +++ b/doc/arguments.md @@ -0,0 +1,159 @@ +[[prev](callpolicies.md)][[top](README.md)][[next](threads.md)] + +#### Advanced argument mapping + +Besides the usual role of providing mapping to existing functions, +cpptcl also allows C++ functions to be written that are specifically designed for interfacing TCL. + +The calling conventions are communicated to cpptcl as part of the function's signature. + +#### Tcl::opt + +``` +void print_hello(std::string & const message, Tcl::opt const & ntimes) { + for (int i = 0; i < (ntimes ? *ntimes : 1); ++i) { + std::cout << message << "\n"; + } +} +``` + +This function when registered with cpptcl can be called as + +``` +print_hello word_up + +print_hello word_up 10 +``` + +#### Tcl::variadic + +``` +void print_hello(Tcl::variadic const & messages) { + for (auto const & m : messages) { + std::cout << m << "\n"; + } +} +``` + +``` +print_hello word up +print_hello +print_hello Yoh pretty ladies around the world +``` + +The variadic argument can only appear once and it has to be the last argument. + +#### Tcl::list + +``` +void print_hello(Tcl::list const & messages) { + for (auto const & m : messages) { + std::cout << m << "\n"; + } +} +``` + +``` +print_hello { w o r d up } +print_hello [split "Wave your hands in the air" { }] +``` + +list is somewhat similar to variadic, but list can be specified multiple times and it +interprets a single argument as a list, whereas variadic maps to multiple arguments + +#### Object types + +tcl allows objects to be treated in much the same way as strings. When the tcl interpreter +is supplied with a function that generates a string representation of an object, the object +type can be attached to that scalar. + +For these examples, the *_ops structure can be imagined as providing functinality to generate +the string representation. For each registred type the tcl interpreter will allocate a typeobj +structure that has a unique memory address. + +``` +Tcl::interpreter interp(Tcl_CreateInterp(), true); + +interp.type("cell", (Component *)); +interp.type("pin", (Pin *)); +interp.type("net", (Net *)); + + + +Component * get_cell(std::string const & name) { + return all_my_cells[name]; +} +void number_of_pins(Component * c) { + std::cout << "cell " << c->name << " has " << c->num_pins << " pins\n"; +} + +number_of_pins [get_cell CPU] +``` + +#### Tcl::any + +To take a limited set of types as an argument. + +``` +void print_object(Tcl::any const & obj) { + if (Component * c = obj.as()) { + std::cout << c->name; + } else if (Net * n = obj.as()) { + std::cout << n->name; + } else if (Pin * p = obj.as()) { + std::cout << p->name; + } + + // or, alternatively + obj.visit([&](Component * c) { std::cout << c->name; }, + [&](Net * n) { std::cout << n->name; }, + [&](Pin * p) { std::cout << p->name; }); +} + +print_obj [get_cell PCH] +print_obj [get_net GND] +``` + +### Tcl::getopt + +This is the same as Tcl::opt, but when the procedure is called a getopt parser +is used to map tcl arguments to C++ arguments and the position of parameters +no longer maps trivially between function call and function implementation. + +``` +void fancy_print_hello(Tcl::getopt const & header, Tcl::getopt const & signature, std::string const & message) { + if (header) { + std::cout << "Now hear this:\n"; + } + std::cout << message << "\n"; + if (signature) { + std::cout << "\t" << *signature << "\n"; + } +} + +fancy_print_hello -signature Everybody -header "When you hear the call you got to get it underway" +``` + +#### Combinations + +Tcl::opt and Tcl::getopt can encapsulate Tcl::any and Tcl::list. + +``` +void optional_hello(Tcl::opt > const & obj) { + if (obj) { + obj->visit(...); + } else { + std::cout << "nothing to see here\n"; + } +} +``` + +Tcl::list can encapsulate Tcl::any. A list of objects belonging to a type in the set. +Tcl::any can encapsulate Tcl::list. A list of objects belonging to a type in the set and all elements +in the list are of the same type. + +Tcl::opt can encapsulate a Tcl::any. and so forth. + +Tcl::any and Tcl::list both cannot encapsulate a Tcl::opt or Tcl::getopt. Tcl::opt cannot encapsulate Tcl::getopt. +Neither can Tcl::getopt encapsulate Tcl::opt. The result of undefined (and frankly nonsensical) combinations are +undefined. TODO: turn them into compile time errors. \ No newline at end of file From f62ed37b2c43929c9a09789a74ccc0fa7e25ea8e Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Wed, 3 Nov 2021 00:31:18 +0100 Subject: [PATCH 05/10] fix build errors and testcases --- CMakeLists.txt | 7 +- cpptcl.cc | 7 + cpptcl/cpptcl.h | 429 +++++++++++++++++++++++--------- examples/Makefile | 524 ++++++++++++++++++++++++++++++++++++++- test/Makefile | 618 +++++++++++++++++++++++++++++++++++++++++++++- test/test7.cc | 2 +- 6 files changed, 1446 insertions(+), 141 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a10c2f2..3479058 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ include(cmake/version.cmake) load_git_properties(cpptcl ${CMAKE_BINARY_DIR}/generated) set(CPPTCL_VERSION 2.2.5) +set(CMAKE_CXX_STANDARD 17) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to Release as none was specified.") @@ -71,7 +72,7 @@ list(APPEND HDRS ${cpptcl_SOURCE_DIR}/cpptcl/version.h) add_library(cpptcl SHARED ${SRCS} ${HDRS} ${HDRS_DETAILS}) add_library(cpptcl::cpptcl ALIAS cpptcl) -target_compile_features(cpptcl PUBLIC cxx_std_11) +target_compile_features(cpptcl PUBLIC cxx_std_17) set_target_properties(cpptcl PROPERTIES CXX_EXTENSIONS OFF CXX_STANDARD_REQUIRED ON) @@ -85,7 +86,7 @@ target_link_libraries(cpptcl PUBLIC ${TCL_STUB_LIBRARY}) add_library(cpptcl_static STATIC ${SRCS} ${HDRS} ${HDRS_DETAILS}) add_library(cpptcl::cpptcl_static ALIAS cpptcl_static) -target_compile_features(cpptcl_static PUBLIC cxx_std_11) +target_compile_features(cpptcl_static PUBLIC cxx_std_17) set_target_properties(cpptcl_static PROPERTIES POSITION_INDEPENDENT_CODE ON CXX_EXTENSIONS OFF @@ -99,7 +100,7 @@ target_include_directories(cpptcl_static PUBLIC ${TCL_INCLUDE_PATH}) add_library(cpptcl_runtime STATIC ${cpptcl_SOURCE_DIR}/cpptcl_runtime.c) add_library(cpptcl::cpptcl_runtime ALIAS cpptcl_runtime) -target_compile_features(cpptcl_runtime PUBLIC cxx_std_11) +target_compile_features(cpptcl_runtime PUBLIC cxx_std_17) set_target_properties(cpptcl_runtime PROPERTIES POSITION_INDEPENDENT_CODE ON CXX_EXTENSIONS OFF diff --git a/cpptcl.cc b/cpptcl.cc index b74b866..81c60cf 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -220,6 +220,11 @@ policies &policies::sink(int index) { return *this; } +policies & policies::options(string const & options) { + options_ = options; + return *this; +} + #if 0 policies &policies::variadic() { variadic_ = true; @@ -238,6 +243,8 @@ policies Tcl::sink(int index) { return policies().sink(index); } //policies Tcl::variadic() { return policies().variadic(); } +policies Tcl::options(std::string const & opt) { return policies().options(opt); } + policies Tcl::usage(string const &message) { return policies().usage(message); } class_handler_base::class_handler_base() { diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index 13b2567..8bab248 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -75,10 +75,13 @@ struct policies { policies &usage(std::string const &message); + policies &options(std::string const & opts); + std::string factory_; std::vector sinks_; bool variadic_; std::string usage_; + std::string options_; }; // syntax short-cuts @@ -86,6 +89,7 @@ policies factory(std::string const &name); policies sink(int index); //policies variadic(); policies usage(std::string const &message); +policies options(std::string const & options); class interpreter; class object; @@ -294,12 +298,31 @@ struct list { interpreter * interp_; Tcl_Obj * lo_; Tcl_ObjType * ot_; - + bool list_owner_ = false; + static const bool isany = details::is_any::value; list() : interp_(nullptr) { } - list(interpreter * interp, Tcl_Obj * lo, Tcl_ObjType * ot) : interp_(interp), lo_(lo), ot_(ot) { } + list(interpreter * interp, Tcl_Obj * lo, Tcl_ObjType * ot, bool list_owner = false) : interp_(interp), lo_(lo), ot_(ot), list_owner_(list_owner) { + if (list_owner) { + Tcl_IncrRefCount(lo); + } + } + + list(list const & o) : interp_(o.interp_), lo_(o.lo_), ot_(o.ot_), list_owner_(o.list_owner_) { + if (list_owner_) { + //std::cerr << "copy list\n"; + Tcl_IncrRefCount(lo_); + } + } + ~list() { + if (list_owner_) { + //std::cerr << "~list " << lo_->refCount << "\n"; + Tcl_DecrRefCount(lo_); + } + } + //list(list const & other) : ot_(other.ot_), interp_(other.interp_), lo_(other.lo_) { std::cerr << '#'; } typedef typename std::conditional::value, T, T *>::type return_t; @@ -310,7 +333,7 @@ struct list { struct iterator { const list * l; - int ix; + std::size_t ix; return_t operator*() const { return l->at(ix); } @@ -353,6 +376,16 @@ struct variadic { operator bool() const { return objc; } }; +// allow variadic() to be used to indicate traditional variadic policies +struct variadic_compat_tag; +variadic() -> variadic; +template <> +struct variadic : public policies { + variadic() { + variadic_ = true; + } +}; + template struct overload { @@ -452,14 +485,31 @@ struct any : public details::any_impl { typedef details::any_impl super_t; any() { super_t::which_ = -1; } interpreter * interp_; - - any(interpreter * i, Tcl_Obj * o) : interp_(i) { + bool list_owner_ = false; + + any(interpreter * i, Tcl_Obj * o, bool list_owner = false) : interp_(i), list_owner_(list_owner) { //this->p_ = o->internalRep.otherValuePtr; this->o_ = o; if (o) { super_t::set_which(i, o); } + if (list_owner) { + Tcl_IncrRefCount(this->o_); + } + } + any(any const & o) : interp_(o.interp_), list_owner_(o.list_owner_) { + this->o_ = o.o_; + this->which_ = o.which_; + if (list_owner_) { + Tcl_IncrRefCount(this->o_); + } + } + ~any() { + if (list_owner_) { + Tcl_DecrRefCount(this->o_); + } } + operator bool() const { return super_t::which_ != -1; } @@ -711,12 +761,12 @@ template class callbac interpreter * interpreter_; public : - callback_v(interpreter * i, functor_type f, policies const & pol, const char * opts = nullptr) : interpreter_(i), policies_(pol), f_(f) { + callback_v(interpreter * i, functor_type f, policies const & pol) : interpreter_(i), policies_(pol), f_(f) { if (num_opt) { - if (! opts) { + if (pol.options_.empty()) { throw tcl_error("no getopt string supplied for function taking getopt<> arguments"); } - generate_hasarg<0, Ts...>::invoke(has_arg_, opts_, opts); + generate_hasarg<0, Ts...>::invoke(has_arg_, opts_, pol.options_.c_str()); } } template struct void_return { }; @@ -984,32 +1034,32 @@ class interpreter { template class class_definer { public: - class_definer(std::shared_ptr> ch, interpreter * inter = nullptr) : ch_(ch), interp_(inter) {} + class_definer(std::shared_ptr> ch, interpreter * inter) : ch_(ch), interp_(inter) {} template - class_definer & def(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { - return def2(name, fn, p, opts); + class_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { + return def2(name, fn, p); } template - class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies(), const char * opts = nullptr) { - ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p, opts))); + class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies()) { + ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p))); return *this; } template - class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies(), const char * opts = nullptr) { - ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p, opts))); + class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies()) { + ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p))); return *this; } template - class_definer & def2(std::string const & name, std::function fn, policies const & p = policies(), const char * opts = nullptr) { - ch_->register_method(name, std::shared_ptr(new details::callback_v, std::function, R, Ts...>(interp_, fn, p, opts))); + class_definer & def2(std::string const & name, std::function fn, policies const & p = policies()) { + ch_->register_method(name, std::shared_ptr(new details::callback_v, std::function, R, Ts...>(interp_, fn, p))); return *this; } template - class_definer & def2(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { - return def2(name, typename details::std_function::type(fn), p, opts); + class_definer & def2(std::string const & name, Fn fn, policies const & p = policies()) { + return def2(name, typename details::std_function::type(fn), p); } template @@ -1024,8 +1074,8 @@ class interpreter { function_definer(C * this_p, interpreter * interp) : this_p_(this_p), interp_(interp) { } template - function_definer & def(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { - interp_->def(name, fn, this_p_, p, opts); + function_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { + interp_->def(name, fn, this_p_, p); return *this; } @@ -1047,22 +1097,22 @@ class interpreter { // free function definitions template - void def(std::string const & name, R(*f)(Ts...), policies const & p = policies(), const char * opts = nullptr) { - add_function(name, new details::callback_v(this, f, p, opts)); + void def(std::string const & name, R(*f)(Ts...), policies const & p = policies()) { + add_function(name, new details::callback_v(this, f, p)); } template - void def(std::string const & name, std::function fn, policies const & p = policies(), const char * opts = nullptr) { - add_function(name, new details::callback_v, R, Ts...>(this, fn, p, opts)); + void def(std::string const & name, std::function fn, policies const & p = policies()) { + add_function(name, new details::callback_v, R, Ts...>(this, fn, p)); } template - void def(std::string const & name, Fn fn, policies const & p = policies(), const char * opts = nullptr) { - def(name, typename details::std_function::type(fn), p, opts); + void def(std::string const & name, Fn fn, policies const & p = policies()) { + def(name, typename details::std_function::type(fn), p); } template - void def(std::string const & name, R (C::*f)(Ts...), C * this_p, policies const & p = policies(), const char * opts = nullptr) { - def(name, [this_p, f](Ts... args) { return (this_p->*f)(args...); }, p, opts); + void def(std::string const & name, R (C::*f)(Ts...), C * this_p, policies const & p = policies()) { + def(name, [this_p, f](Ts... args) { return (this_p->*f)(args...); }, p); } template @@ -1088,19 +1138,86 @@ class interpreter { add_constructor(name, ch, new callback_type(this, &call_constructor::doit, p), p); - return class_definer(ch); + return class_definer(ch, this); } std::map obj_type_by_tclname_; std::map obj_type_by_cppname_; + template + struct deleter { + virtual bool free(Tcl_Obj * o) { + delete (T *) o->internalRep.twoPtrValue.ptr1; + return false; + } + virtual deleter * dup_deleter() { return this; } + virtual void * dup(void * obj) { + return (void *) new T(*((T *) obj)); + } + virtual ~deleter() { } + }; + + template + struct dyn_deleter : public deleter { + bool free(Tcl_Obj * o) override { + //std::cerr << "REF#: " << o->refCount << "\n"; + delete (T *) o->internalRep.twoPtrValue.ptr1; + return true; + } + deleter * dup_deleter() override { return new dyn_deleter(); } + }; + + template + struct shared_ptr_deleter : public deleter { + std::shared_ptr p_; + + shared_ptr_deleter(T * p) : p_(p) { } + + template + shared_ptr_deleter(T * p, DEL del) : p_(p, del) { + //std::cerr << "custom\n"; + } + shared_ptr_deleter(std::shared_ptr & p) : p_(p) { + //std::cerr << "copy\n"; + } + bool free(Tcl_Obj * o) override { + return true; + } + virtual void * dup(void * obj) override { return obj; } + deleter * dup_deleter() override { + //std::cerr << "dup\n"; + return new shared_ptr_deleter(p_); + } + + ~shared_ptr_deleter() { + //std::cerr << "~shptr deleter\n"; + } + }; + + template struct type_ops { static void dup(Tcl_Obj * src, Tcl_Obj * dst) { - dst->internalRep.otherValuePtr = src->internalRep.otherValuePtr; + //2p dst->internalRep.otherValuePtr = src->internalRep.otherValuePtr; + if (src->internalRep.twoPtrValue.ptr2) { + //std::cerr << "dup osrc=" << src << "[" << src->refCount << "] odst=" << dst << "[" << dst->refCount << "]\n";// type=" << src->typePtr << " intern=" << src->internalRep.otherValuePtr << "\n"; + deleter * d = (deleter *) src->internalRep.twoPtrValue.ptr2; + dst->internalRep.twoPtrValue.ptr1 = d->dup(src->internalRep.twoPtrValue.ptr1); + dst->internalRep.twoPtrValue.ptr2 = (( deleter *)src->internalRep.twoPtrValue.ptr2)->dup_deleter(); + } else { + dst->internalRep.twoPtrValue.ptr1 = src->internalRep.twoPtrValue.ptr1; + dst->internalRep.twoPtrValue.ptr2 = nullptr; + } + //src->internalRep.twoPtrValue.ptr2 = nullptr; dst->typePtr = src->typePtr; - //std::cerr << "dup osrc=" << src << " odst=" << dst << " type=" << src->typePtr << " intern=" << src->internalRep.otherValuePtr << "\n"; } - static void free(Tcl_Obj *) { + static void free(Tcl_Obj * o) { + if (o->internalRep.twoPtrValue.ptr2) { + //std::cerr << "free " << o << "\n"; + deleter * d = (deleter *) o->internalRep.twoPtrValue.ptr2; + if (d->free(o)) { + delete d; + } + } } static void str(Tcl_Obj *) { } @@ -1142,7 +1259,8 @@ class interpreter { template T * try_type(Tcl_Obj * o) { if (is_type(o)) { - return (T *) o->internalRep.otherValuePtr; + //2p return (T *) o->internalRep.otherValuePtr; + return (T *) o->internalRep.twoPtrValue.ptr1; } return nullptr; } @@ -1192,7 +1310,8 @@ class interpreter { if (it->second == o->typePtr) { //return (typename std::conditional::value, T, T*>::type) o->internalRep.otherValuePtr; //std::cerr << "objcast " << o << " " << o->internalRep.otherValuePtr << "\n"; - return (T) o->internalRep.otherValuePtr; + //return (T) o->internalRep.otherValuePtr; + return (T) o->internalRep.twoPtrValue.ptr1;//otherValuePtr; } else if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { int len; if (Tcl_ListObjLength(i, o, &len) == TCL_OK) { @@ -1201,7 +1320,8 @@ class interpreter { if (Tcl_ListObjIndex(i, o, 0, &o2) == TCL_OK) { if (it->second == o2->typePtr) { //std::cerr << "objcast(list=len1) " << o2 << " " << o2->internalRep.otherValuePtr << "\n"; - return (T) o2->internalRep.otherValuePtr; + //2p return (T) o2->internalRep.otherValuePtr; + return (T) o2->internalRep.twoPtrValue.ptr1; } } } else { @@ -1233,7 +1353,7 @@ class interpreter { add_class(name, ch); - return class_definer(ch); + return class_definer(ch, this); } // free script evaluation @@ -1245,9 +1365,18 @@ class interpreter { // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result eval(InputIterator first, InputIterator last); + + template + object makeobj(OT * n, bool owning = false, DEL delfn = []{}); + template + object makeobj(OT * n, bool owning = false); + + template void setVar(std::string const & name, T const & value); - + template + void setVar(std::string const & name, object & ptr); + // Get a variable from TCL interpreter with Tcl_GetVar details::result getVar(std::string const &scalarTclVariable); details::result getVar(std::string const &arrayTclVariable, std::string const &arrayIndex); @@ -1305,7 +1434,8 @@ typename list::return_t list::at(std::size_t ix) const { return interp_->template tcl_cast(o); } else { if (o->typePtr == ot_) { - return (return_t) o->internalRep.otherValuePtr; + //return (return_t) o->internalRep.otherValuePtr; + return (return_t) o->internalRep.twoPtrValue.ptr1; } else { throw tcl_error("Unexpected type in list"); } @@ -1327,7 +1457,8 @@ typename std::conditional::value, TT, TT *>::type anyo_->typePtr ? this->o_->typePtr->name : "(none)") + " to list"); } } else { - return (TT *) this->o_->internalRep.otherValuePtr; //o_->internalRep.otherValuePtr; + //return (TT *) this->o_->internalRep.otherValuePtr; //o_->internalRep.otherValuePtr; + return (TT *) this->o_->internalRep.twoPtrValue.ptr1; //o_->internalRep.otherValuePtr; } } if constexpr (details::is_list::value) { @@ -1365,6 +1496,96 @@ inline void any_impl::set_which(interpreter * i, Tcl_Obj * o) { } } +template +struct back { + typedef void type; +}; + +template +struct back { + using type = typename std::conditional::type>::type; +}; + +template +void callback_v::invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv []) { + static const int num_gopt = num_getopt::value; + static const int num_opt = num_optional::value; + Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; + Tcl_Obj boolean_dummy_object; + std::size_t ai = std::is_same::value ? 1 : 2; + static const bool has_variadic_ = has_variadic::value; + + if (interpreter_->trace()) { + for (int i = 0; i < argc; ++i) { + std::cerr << " "; + if (argv[i]->typePtr && strcmp(argv[i]->typePtr->name, "list") == 0) { + std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; + } else { + std::cerr << Tcl_GetString(argv[i]); + } + } + std::cerr << "\n"; + } + + if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0) { + if (num_gopt) { + std::cerr << Tcl_GetString(argv[0]); + for (int oi = 0; oi < num_gopt; ++oi) { + interpreter_->terr() << " -" << opts_[oi] << (has_arg_[oi] ? " " : ""); + } + + if (policies_.variadic_) { + interpreter_->terr() << " objects..."; + } + interpreter_->terr() << "\n"; + } + return; + } + + if (num_gopt) { + for (; ai < argc; ++ai) { + if (argv[ai]->typePtr) break; + char * s = Tcl_GetString(argv[ai]); + //std::cerr << "look at: " << s << "\n"; + if (s[0] == '-') { + if (s[1] == '-' && s[2] == 0) { + ++ai; + break; + } + bool found = false; + for (int oi = 0; oi < num_gopt; ++oi) { + if (opts_[oi].compare(s + 1) == 0) { + found = true; + //std::cerr << s << " at " << ai << " mapped to " << oi << " has_arg " << has_arg_[oi] << "\n"; + if (has_arg_[oi]) { + ++ai; + if (ai >= argc) { + throw tcl_error(std::string("option requires an argument: ") + s); + } + getopt_argv[oi] = argv[ai]; + } else { + getopt_argv[oi] = &boolean_dummy_object; + } + } + } + if (!found) { + throw tcl_error(std::string("unknown option: ") + s + " at index " + std::to_string(ai)); + } + } else { + break; + } + } + } + check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ || policies_.variadic_ ? 1 : 0) - num_gopt - num_opt, has_variadic_ || policies_.variadic_ ? -1 : sizeof...(Ts) - num_gopt, policies_.usage_); + do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + post_process_policies(interp, policies_, argv + ai, false); +} +} + +#include "cpptcl/cpptcl_object.h" + + +namespace details { template typename details::fix_variadic_return::type do_cast(interpreter * tcli, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol) { typedef typename remove_rc::type TT; @@ -1372,6 +1593,8 @@ typename details::fix_variadic_return::type do_cast(interpr static const bool is_variadic_arg = is_variadic::value; static const bool is_optional_arg = is_optional::value; static const bool is_getopt_arg = is_getopt::value; + + static const bool is_old_variadic_arg = Ii + 1 == In && std::is_same::type, object const &>::value; typedef typename list_unpack::type list_unpack_t; static const bool is_list_arg = !std::is_same::value; @@ -1382,7 +1605,13 @@ typename details::fix_variadic_return::type do_cast(interpr typedef typename optional_unpack::type opt_unpack_t; - //std::cerr << "do_cast " << Ii << " " << ArgOffset << " " << objc << "\n"; + //std::cerr << "do_cast argi=" << Ii << "/" << In << " argoff=" << ArgOffset << " objc=" << objc << " " << typeid(TTArg).name() << "\n"; + if constexpr (is_old_variadic_arg) { + if (pol.variadic_) { + return details::get_var_params(tcli->get(), objc, objv, Ii + ArgOffset, pol); + } + } + if constexpr (is_optional_arg || is_getopt_arg) { if constexpr (is_getopt_arg) { static const int getopti = getopt_index<0, Ii, 0, Ts...>::value; @@ -1418,7 +1647,7 @@ typename details::fix_variadic_return::type do_cast(interpr } else { Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); //std::cerr << "create listone " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << (ot ? ot->name : "") << "\n"; - return TT(tcli, lo, ot); + return TT(tcli, lo, ot, true); return return_dummy_value::doit(tcli, lo, ot); } } else { @@ -1454,7 +1683,7 @@ typename details::fix_variadic_return::type do_cast(interpr } else { if (is_any_list_arg) { Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); - return TT(tcli, lo); + return TT(tcli, lo, true); } else { return TT(tcli, objv[Ii + ArgOffset]); } @@ -1484,83 +1713,7 @@ typename details::fix_variadic_return::type do_cast(interpr } } } - -template -void callback_v::invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv []) { - static const int num_gopt = num_getopt::value; - static const int num_opt = num_optional::value; - Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; - Tcl_Obj boolean_dummy_object; - std::size_t ai = std::is_same::value ? 1 : 2; - static const bool has_variadic_ = has_variadic::value; - - if (interpreter_->trace()) { - for (int i = 0; i < argc; ++i) { - std::cerr << " "; - if (argv[i]->typePtr && strcmp(argv[i]->typePtr->name, "list") == 0) { - std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; - } else { - std::cerr << Tcl_GetString(argv[i]); - } - } - std::cerr << "\n"; - } - - if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0) { - if (num_gopt) { - std::cerr << Tcl_GetString(argv[0]); - for (int oi = 0; oi < num_gopt; ++oi) { - interpreter_->terr() << " -" << opts_[oi] << (has_arg_[oi] ? " " : ""); - } - - if (policies_.variadic_) { - interpreter_->terr() << " objects..."; - } - interpreter_->terr() << "\n"; - } - return; - } - - if (num_gopt) { - for (; ai < argc; ++ai) { - char * s = Tcl_GetString(argv[ai]); - //std::cerr << "look at: " << s << "\n"; - if (s[0] == '-') { - if (s[1] == '-' && s[2] == 0) { - ++ai; - break; - } - bool found = false; - for (int oi = 0; oi < num_gopt; ++oi) { - if (opts_[oi].compare(s + 1) == 0) { - found = true; - //std::cerr << s << " at " << ai << " mapped to " << oi << " has_arg " << has_arg_[oi] << "\n"; - if (has_arg_[oi]) { - ++ai; - if (ai >= argc) { - throw tcl_error(std::string("option requires an argument: ") + s); - } - getopt_argv[oi] = argv[ai]; - } else { - getopt_argv[oi] = &boolean_dummy_object; - } - } - } - if (!found) { - throw tcl_error(std::string("unknown option: ") + s); - } - } else { - break; - } - } - } - check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ ? 1 : 0) - num_gopt - num_opt, has_variadic_ ? -1 : sizeof...(Ts) - num_gopt, policies_.usage_); - do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); - post_process_policies(interp, policies_, argv + ai, false); } -} - -#include "cpptcl/cpptcl_object.h" template void interpreter::defvar(std::string const & name, T & v) { @@ -1599,6 +1752,44 @@ void interpreter::setVar(std::string const & name, T const & value) { Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, val_obj.get_object(), 0); } +template +object interpreter::makeobj(OT * n, bool owning, DEL delfn) { + Tcl_Obj * o = Tcl_NewObj(); + + o->internalRep.twoPtrValue.ptr1 = (void *) n; + if (owning) { + if constexpr (std::is_same::value) { + if (delfn) { } + o->internalRep.twoPtrValue.ptr2 = new interpreter::shared_ptr_deleter(n); //new interpreter::dyn_deleter(); //nullptr; + } else { + o->internalRep.twoPtrValue.ptr2 = new interpreter::shared_ptr_deleter(n, delfn); //new interpreter::dyn_deleter(); //nullptr; + } + //o->internalRep.twoPtrValue.ptr2 = new interpreter::dyn_deleter(); //nullptr; + } else { + o->internalRep.twoPtrValue.ptr2 = nullptr; + } + + auto it = this->obj_type_by_cppname_.find(std::type_index(typeid(OT))); + bool ok = it != this->obj_type_by_cppname_.end(); + if (ok) { + o->typePtr = it->second; + } else { + throw tcl_error("type lookup failed"); + } + Tcl_InvalidateStringRep(o); + return object(o, true); +} +template +object interpreter::makeobj(OT * n, bool owning) { + return makeobj(n, owning, (void *) 0 ); +} +template +void interpreter::setVar(std::string const & name, object & obj) { + object name_obj(name); + Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, obj.get_object(), 0); +} + + template struct Bind { object cmd_; diff --git a/examples/Makefile b/examples/Makefile index ddb5d36..f9db266 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,16 +1,518 @@ -OUTPUTS = build/example2 build/example6 +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.16 -all : ${OUTPUTS} +# Default target executed when no arguments are given to make. +default_target: all -build/Makefile: - mkdir -p build - (cd build; cmake ..) +.PHONY : default_target -build/example2 : build/Makefile example2.cc - (cd build; make example2) +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: -build/example6 : build/Makefile example6.cc - (cd build; make example6) -clean : - rm -rf build +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/sascha/git/cpptcl + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/sascha/git/cpptcl + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target install/strip +install/strip: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip + +# Special rule for the target install/strip +install/strip/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip/fast + +# Special rule for the target test +test: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running tests..." + /usr/bin/ctest --force-new-ctest-process $(ARGS) +.PHONY : test + +# Special rule for the target test +test/fast: test + +.PHONY : test/fast + +# Special rule for the target install +install: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install + +# Special rule for the target install +install/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target list_install_components +list_install_components: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\"" +.PHONY : list_install_components + +# Special rule for the target list_install_components +list_install_components/fast: list_install_components + +.PHONY : list_install_components/fast + +# Special rule for the target install/local +install/local: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local + +# Special rule for the target install/local +install/local/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local/fast + +# The main all target +all: cmake_check_build_system + cd /home/sascha/git/cpptcl && $(CMAKE_COMMAND) -E cmake_progress_start /home/sascha/git/cpptcl/CMakeFiles /home/sascha/git/cpptcl/examples/CMakeFiles/progress.marks + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/all + $(CMAKE_COMMAND) -E cmake_progress_start /home/sascha/git/cpptcl/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /home/sascha/git/cpptcl && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +examples/CMakeFiles/cpptcl_example_functions.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/cpptcl_example_functions.dir/rule +.PHONY : examples/CMakeFiles/cpptcl_example_functions.dir/rule + +# Convenience name for target. +cpptcl_example_functions: examples/CMakeFiles/cpptcl_example_functions.dir/rule + +.PHONY : cpptcl_example_functions + +# fast build rule for target. +cpptcl_example_functions/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_example_functions.dir/build.make examples/CMakeFiles/cpptcl_example_functions.dir/build +.PHONY : cpptcl_example_functions/fast + +# Convenience name for target. +examples/CMakeFiles/example2.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/example2.dir/rule +.PHONY : examples/CMakeFiles/example2.dir/rule + +# Convenience name for target. +example2: examples/CMakeFiles/example2.dir/rule + +.PHONY : example2 + +# fast build rule for target. +example2/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example2.dir/build.make examples/CMakeFiles/example2.dir/build +.PHONY : example2/fast + +# Convenience name for target. +examples/CMakeFiles/example6.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/example6.dir/rule +.PHONY : examples/CMakeFiles/example6.dir/rule + +# Convenience name for target. +example6: examples/CMakeFiles/example6.dir/rule + +.PHONY : example6 + +# fast build rule for target. +example6/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example6.dir/build.make examples/CMakeFiles/example6.dir/build +.PHONY : example6/fast + +# Convenience name for target. +examples/CMakeFiles/cpptcl_module_two.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/cpptcl_module_two.dir/rule +.PHONY : examples/CMakeFiles/cpptcl_module_two.dir/rule + +# Convenience name for target. +cpptcl_module_two: examples/CMakeFiles/cpptcl_module_two.dir/rule + +.PHONY : cpptcl_module_two + +# fast build rule for target. +cpptcl_module_two/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_two.dir/build.make examples/CMakeFiles/cpptcl_module_two.dir/build +.PHONY : cpptcl_module_two/fast + +# Convenience name for target. +examples/CMakeFiles/cpptcl_module_three.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/cpptcl_module_three.dir/rule +.PHONY : examples/CMakeFiles/cpptcl_module_three.dir/rule + +# Convenience name for target. +cpptcl_module_three: examples/CMakeFiles/cpptcl_module_three.dir/rule + +.PHONY : cpptcl_module_three + +# fast build rule for target. +cpptcl_module_three/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_three.dir/build.make examples/CMakeFiles/cpptcl_module_three.dir/build +.PHONY : cpptcl_module_three/fast + +# Convenience name for target. +examples/CMakeFiles/cpptcl_module_five.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/cpptcl_module_five.dir/rule +.PHONY : examples/CMakeFiles/cpptcl_module_five.dir/rule + +# Convenience name for target. +cpptcl_module_five: examples/CMakeFiles/cpptcl_module_five.dir/rule + +.PHONY : cpptcl_module_five + +# fast build rule for target. +cpptcl_module_five/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_five.dir/build.make examples/CMakeFiles/cpptcl_module_five.dir/build +.PHONY : cpptcl_module_five/fast + +# Convenience name for target. +examples/CMakeFiles/cpptcl_module_six.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 examples/CMakeFiles/cpptcl_module_six.dir/rule +.PHONY : examples/CMakeFiles/cpptcl_module_six.dir/rule + +# Convenience name for target. +cpptcl_module_six: examples/CMakeFiles/cpptcl_module_six.dir/rule + +.PHONY : cpptcl_module_six + +# fast build rule for target. +cpptcl_module_six/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_six.dir/build.make examples/CMakeFiles/cpptcl_module_six.dir/build +.PHONY : cpptcl_module_six/fast + +cpptcl_example_functions.o: cpptcl_example_functions.cc.o + +.PHONY : cpptcl_example_functions.o + +# target to build an object file +cpptcl_example_functions.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_example_functions.dir/build.make examples/CMakeFiles/cpptcl_example_functions.dir/cpptcl_example_functions.cc.o +.PHONY : cpptcl_example_functions.cc.o + +cpptcl_example_functions.i: cpptcl_example_functions.cc.i + +.PHONY : cpptcl_example_functions.i + +# target to preprocess a source file +cpptcl_example_functions.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_example_functions.dir/build.make examples/CMakeFiles/cpptcl_example_functions.dir/cpptcl_example_functions.cc.i +.PHONY : cpptcl_example_functions.cc.i + +cpptcl_example_functions.s: cpptcl_example_functions.cc.s + +.PHONY : cpptcl_example_functions.s + +# target to generate assembly for a file +cpptcl_example_functions.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_example_functions.dir/build.make examples/CMakeFiles/cpptcl_example_functions.dir/cpptcl_example_functions.cc.s +.PHONY : cpptcl_example_functions.cc.s + +cpptcl_module_five.o: cpptcl_module_five.cc.o + +.PHONY : cpptcl_module_five.o + +# target to build an object file +cpptcl_module_five.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_five.dir/build.make examples/CMakeFiles/cpptcl_module_five.dir/cpptcl_module_five.cc.o +.PHONY : cpptcl_module_five.cc.o + +cpptcl_module_five.i: cpptcl_module_five.cc.i + +.PHONY : cpptcl_module_five.i + +# target to preprocess a source file +cpptcl_module_five.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_five.dir/build.make examples/CMakeFiles/cpptcl_module_five.dir/cpptcl_module_five.cc.i +.PHONY : cpptcl_module_five.cc.i + +cpptcl_module_five.s: cpptcl_module_five.cc.s + +.PHONY : cpptcl_module_five.s + +# target to generate assembly for a file +cpptcl_module_five.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_five.dir/build.make examples/CMakeFiles/cpptcl_module_five.dir/cpptcl_module_five.cc.s +.PHONY : cpptcl_module_five.cc.s + +cpptcl_module_six.o: cpptcl_module_six.cc.o + +.PHONY : cpptcl_module_six.o + +# target to build an object file +cpptcl_module_six.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_six.dir/build.make examples/CMakeFiles/cpptcl_module_six.dir/cpptcl_module_six.cc.o +.PHONY : cpptcl_module_six.cc.o + +cpptcl_module_six.i: cpptcl_module_six.cc.i + +.PHONY : cpptcl_module_six.i + +# target to preprocess a source file +cpptcl_module_six.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_six.dir/build.make examples/CMakeFiles/cpptcl_module_six.dir/cpptcl_module_six.cc.i +.PHONY : cpptcl_module_six.cc.i + +cpptcl_module_six.s: cpptcl_module_six.cc.s + +.PHONY : cpptcl_module_six.s + +# target to generate assembly for a file +cpptcl_module_six.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_six.dir/build.make examples/CMakeFiles/cpptcl_module_six.dir/cpptcl_module_six.cc.s +.PHONY : cpptcl_module_six.cc.s + +cpptcl_module_three.o: cpptcl_module_three.cc.o + +.PHONY : cpptcl_module_three.o + +# target to build an object file +cpptcl_module_three.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_three.dir/build.make examples/CMakeFiles/cpptcl_module_three.dir/cpptcl_module_three.cc.o +.PHONY : cpptcl_module_three.cc.o + +cpptcl_module_three.i: cpptcl_module_three.cc.i + +.PHONY : cpptcl_module_three.i + +# target to preprocess a source file +cpptcl_module_three.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_three.dir/build.make examples/CMakeFiles/cpptcl_module_three.dir/cpptcl_module_three.cc.i +.PHONY : cpptcl_module_three.cc.i + +cpptcl_module_three.s: cpptcl_module_three.cc.s + +.PHONY : cpptcl_module_three.s + +# target to generate assembly for a file +cpptcl_module_three.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_three.dir/build.make examples/CMakeFiles/cpptcl_module_three.dir/cpptcl_module_three.cc.s +.PHONY : cpptcl_module_three.cc.s + +cpptcl_module_two.o: cpptcl_module_two.cc.o + +.PHONY : cpptcl_module_two.o + +# target to build an object file +cpptcl_module_two.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_two.dir/build.make examples/CMakeFiles/cpptcl_module_two.dir/cpptcl_module_two.cc.o +.PHONY : cpptcl_module_two.cc.o + +cpptcl_module_two.i: cpptcl_module_two.cc.i + +.PHONY : cpptcl_module_two.i + +# target to preprocess a source file +cpptcl_module_two.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_two.dir/build.make examples/CMakeFiles/cpptcl_module_two.dir/cpptcl_module_two.cc.i +.PHONY : cpptcl_module_two.cc.i + +cpptcl_module_two.s: cpptcl_module_two.cc.s + +.PHONY : cpptcl_module_two.s + +# target to generate assembly for a file +cpptcl_module_two.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/cpptcl_module_two.dir/build.make examples/CMakeFiles/cpptcl_module_two.dir/cpptcl_module_two.cc.s +.PHONY : cpptcl_module_two.cc.s + +example2.o: example2.cc.o + +.PHONY : example2.o + +# target to build an object file +example2.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example2.dir/build.make examples/CMakeFiles/example2.dir/example2.cc.o +.PHONY : example2.cc.o + +example2.i: example2.cc.i + +.PHONY : example2.i + +# target to preprocess a source file +example2.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example2.dir/build.make examples/CMakeFiles/example2.dir/example2.cc.i +.PHONY : example2.cc.i + +example2.s: example2.cc.s + +.PHONY : example2.s + +# target to generate assembly for a file +example2.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example2.dir/build.make examples/CMakeFiles/example2.dir/example2.cc.s +.PHONY : example2.cc.s + +example6.o: example6.cc.o + +.PHONY : example6.o + +# target to build an object file +example6.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example6.dir/build.make examples/CMakeFiles/example6.dir/example6.cc.o +.PHONY : example6.cc.o + +example6.i: example6.cc.i + +.PHONY : example6.i + +# target to preprocess a source file +example6.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example6.dir/build.make examples/CMakeFiles/example6.dir/example6.cc.i +.PHONY : example6.cc.i + +example6.s: example6.cc.s + +.PHONY : example6.s + +# target to generate assembly for a file +example6.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f examples/CMakeFiles/example6.dir/build.make examples/CMakeFiles/example6.dir/example6.cc.s +.PHONY : example6.cc.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... install/strip" + @echo "... cpptcl_example_functions" + @echo "... example2" + @echo "... example6" + @echo "... cpptcl_module_two" + @echo "... test" + @echo "... cpptcl_module_three" + @echo "... install" + @echo "... edit_cache" + @echo "... rebuild_cache" + @echo "... cpptcl_module_five" + @echo "... list_install_components" + @echo "... cpptcl_module_six" + @echo "... install/local" + @echo "... cpptcl_example_functions.o" + @echo "... cpptcl_example_functions.i" + @echo "... cpptcl_example_functions.s" + @echo "... cpptcl_module_five.o" + @echo "... cpptcl_module_five.i" + @echo "... cpptcl_module_five.s" + @echo "... cpptcl_module_six.o" + @echo "... cpptcl_module_six.i" + @echo "... cpptcl_module_six.s" + @echo "... cpptcl_module_three.o" + @echo "... cpptcl_module_three.i" + @echo "... cpptcl_module_three.s" + @echo "... cpptcl_module_two.o" + @echo "... cpptcl_module_two.i" + @echo "... cpptcl_module_two.s" + @echo "... example2.o" + @echo "... example2.i" + @echo "... example2.s" + @echo "... example6.o" + @echo "... example6.i" + @echo "... example6.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /home/sascha/git/cpptcl && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/test/Makefile b/test/Makefile index 295430a..66a0519 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,11 +1,615 @@ -all: build/Makefile - (cd build; make) +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.16 -build/Makefile: - cmake -Bbuild -H. +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + + +# A target that is always out of date. +cmake_force: + +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/sascha/git/cpptcl + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/sascha/git/cpptcl + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target install/strip +install/strip: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip + +# Special rule for the target install/strip +install/strip/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip/fast + +# Special rule for the target install/local +install/local: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local + +# Special rule for the target install/local +install/local/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local/fast + +# Special rule for the target test +test: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running tests..." + /usr/bin/ctest --force-new-ctest-process $(ARGS) +.PHONY : test + +# Special rule for the target test +test/fast: test + +.PHONY : test/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache + +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache + +.PHONY : rebuild_cache/fast + +# Special rule for the target list_install_components +list_install_components: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Available install components are: \"Unspecified\"" +.PHONY : list_install_components + +# Special rule for the target list_install_components +list_install_components/fast: list_install_components + +.PHONY : list_install_components/fast + +# Special rule for the target install +install: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install + +# Special rule for the target install +install/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install/fast + +# The main all target +all: cmake_check_build_system + cd /home/sascha/git/cpptcl && $(CMAKE_COMMAND) -E cmake_progress_start /home/sascha/git/cpptcl/CMakeFiles /home/sascha/git/cpptcl/test/CMakeFiles/progress.marks + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/all + $(CMAKE_COMMAND) -E cmake_progress_start /home/sascha/git/cpptcl/CMakeFiles 0 +.PHONY : all + +# The main clean target clean: - rm -rf build + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/clean +.PHONY : clean + +# The main clean target +clean/fast: clean + +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /home/sascha/git/cpptcl && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +test/CMakeFiles/test1.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test1.dir/rule +.PHONY : test/CMakeFiles/test1.dir/rule + +# Convenience name for target. +test1: test/CMakeFiles/test1.dir/rule + +.PHONY : test1 + +# fast build rule for target. +test1/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/build +.PHONY : test1/fast + +# Convenience name for target. +test/CMakeFiles/test3.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test3.dir/rule +.PHONY : test/CMakeFiles/test3.dir/rule + +# Convenience name for target. +test3: test/CMakeFiles/test3.dir/rule + +.PHONY : test3 + +# fast build rule for target. +test3/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/build +.PHONY : test3/fast + +# Convenience name for target. +test/CMakeFiles/test4.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test4.dir/rule +.PHONY : test/CMakeFiles/test4.dir/rule + +# Convenience name for target. +test4: test/CMakeFiles/test4.dir/rule + +.PHONY : test4 + +# fast build rule for target. +test4/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/build +.PHONY : test4/fast + +# Convenience name for target. +test/CMakeFiles/test5.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test5.dir/rule +.PHONY : test/CMakeFiles/test5.dir/rule + +# Convenience name for target. +test5: test/CMakeFiles/test5.dir/rule + +.PHONY : test5 + +# fast build rule for target. +test5/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/build +.PHONY : test5/fast + +# Convenience name for target. +test/CMakeFiles/test_main.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test_main.dir/rule +.PHONY : test/CMakeFiles/test_main.dir/rule + +# Convenience name for target. +test_main: test/CMakeFiles/test_main.dir/rule + +.PHONY : test_main + +# fast build rule for target. +test_main/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/build +.PHONY : test_main/fast + +# Convenience name for target. +test/CMakeFiles/test6.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test6.dir/rule +.PHONY : test/CMakeFiles/test6.dir/rule + +# Convenience name for target. +test6: test/CMakeFiles/test6.dir/rule + +.PHONY : test6 + +# fast build rule for target. +test6/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/build +.PHONY : test6/fast + +# Convenience name for target. +test/CMakeFiles/test2.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test2.dir/rule +.PHONY : test/CMakeFiles/test2.dir/rule + +# Convenience name for target. +test2: test/CMakeFiles/test2.dir/rule + +.PHONY : test2 + +# fast build rule for target. +test2/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/build +.PHONY : test2/fast + +# Convenience name for target. +test/CMakeFiles/test7.dir/rule: + cd /home/sascha/git/cpptcl && $(MAKE) -f CMakeFiles/Makefile2 test/CMakeFiles/test7.dir/rule +.PHONY : test/CMakeFiles/test7.dir/rule + +# Convenience name for target. +test7: test/CMakeFiles/test7.dir/rule + +.PHONY : test7 + +# fast build rule for target. +test7/fast: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/build +.PHONY : test7/fast + +__/cpptcl.o: __/cpptcl.cc.o + +.PHONY : __/cpptcl.o + +# target to build an object file +__/cpptcl.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/__/cpptcl.cc.o + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/__/cpptcl.cc.o +.PHONY : __/cpptcl.cc.o + +__/cpptcl.i: __/cpptcl.cc.i + +.PHONY : __/cpptcl.i + +# target to preprocess a source file +__/cpptcl.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/__/cpptcl.cc.i + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/__/cpptcl.cc.i +.PHONY : __/cpptcl.cc.i + +__/cpptcl.s: __/cpptcl.cc.s + +.PHONY : __/cpptcl.s + +# target to generate assembly for a file +__/cpptcl.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/__/cpptcl.cc.s + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/__/cpptcl.cc.s +.PHONY : __/cpptcl.cc.s + +test1.o: test1.cc.o + +.PHONY : test1.o + +# target to build an object file +test1.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/test1.cc.o +.PHONY : test1.cc.o + +test1.i: test1.cc.i + +.PHONY : test1.i + +# target to preprocess a source file +test1.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/test1.cc.i +.PHONY : test1.cc.i + +test1.s: test1.cc.s + +.PHONY : test1.s + +# target to generate assembly for a file +test1.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test1.dir/build.make test/CMakeFiles/test1.dir/test1.cc.s +.PHONY : test1.cc.s + +test2.o: test2.cc.o + +.PHONY : test2.o + +# target to build an object file +test2.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/test2.cc.o +.PHONY : test2.cc.o + +test2.i: test2.cc.i + +.PHONY : test2.i + +# target to preprocess a source file +test2.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/test2.cc.i +.PHONY : test2.cc.i + +test2.s: test2.cc.s + +.PHONY : test2.s + +# target to generate assembly for a file +test2.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test2.dir/build.make test/CMakeFiles/test2.dir/test2.cc.s +.PHONY : test2.cc.s + +test3.o: test3.cc.o + +.PHONY : test3.o + +# target to build an object file +test3.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/test3.cc.o +.PHONY : test3.cc.o + +test3.i: test3.cc.i + +.PHONY : test3.i + +# target to preprocess a source file +test3.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/test3.cc.i +.PHONY : test3.cc.i + +test3.s: test3.cc.s + +.PHONY : test3.s + +# target to generate assembly for a file +test3.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test3.dir/build.make test/CMakeFiles/test3.dir/test3.cc.s +.PHONY : test3.cc.s + +test4.o: test4.cc.o + +.PHONY : test4.o + +# target to build an object file +test4.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/test4.cc.o +.PHONY : test4.cc.o + +test4.i: test4.cc.i + +.PHONY : test4.i + +# target to preprocess a source file +test4.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/test4.cc.i +.PHONY : test4.cc.i + +test4.s: test4.cc.s + +.PHONY : test4.s + +# target to generate assembly for a file +test4.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test4.dir/build.make test/CMakeFiles/test4.dir/test4.cc.s +.PHONY : test4.cc.s + +test5.o: test5.cc.o + +.PHONY : test5.o + +# target to build an object file +test5.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/test5.cc.o +.PHONY : test5.cc.o + +test5.i: test5.cc.i + +.PHONY : test5.i + +# target to preprocess a source file +test5.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/test5.cc.i +.PHONY : test5.cc.i + +test5.s: test5.cc.s + +.PHONY : test5.s + +# target to generate assembly for a file +test5.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test5.dir/build.make test/CMakeFiles/test5.dir/test5.cc.s +.PHONY : test5.cc.s + +test6.o: test6.cc.o + +.PHONY : test6.o + +# target to build an object file +test6.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/test6.cc.o +.PHONY : test6.cc.o + +test6.i: test6.cc.i + +.PHONY : test6.i + +# target to preprocess a source file +test6.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/test6.cc.i +.PHONY : test6.cc.i + +test6.s: test6.cc.s + +.PHONY : test6.s + +# target to generate assembly for a file +test6.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test6.dir/build.make test/CMakeFiles/test6.dir/test6.cc.s +.PHONY : test6.cc.s + +test7.o: test7.cc.o + +.PHONY : test7.o + +# target to build an object file +test7.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/test7.cc.o +.PHONY : test7.cc.o + +test7.i: test7.cc.i + +.PHONY : test7.i + +# target to preprocess a source file +test7.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/test7.cc.i +.PHONY : test7.cc.i + +test7.s: test7.cc.s + +.PHONY : test7.s + +# target to generate assembly for a file +test7.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test7.dir/build.make test/CMakeFiles/test7.dir/test7.cc.s +.PHONY : test7.cc.s + +test_main.o: test_main.cc.o + +.PHONY : test_main.o + +# target to build an object file +test_main.cc.o: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/test_main.cc.o +.PHONY : test_main.cc.o + +test_main.i: test_main.cc.i + +.PHONY : test_main.i + +# target to preprocess a source file +test_main.cc.i: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/test_main.cc.i +.PHONY : test_main.cc.i + +test_main.s: test_main.cc.s + +.PHONY : test_main.s + +# target to generate assembly for a file +test_main.cc.s: + cd /home/sascha/git/cpptcl && $(MAKE) -f test/CMakeFiles/test_main.dir/build.make test/CMakeFiles/test_main.dir/test_main.cc.s +.PHONY : test_main.cc.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... install/strip" + @echo "... install/local" + @echo "... test1" + @echo "... test3" + @echo "... test4" + @echo "... test5" + @echo "... test_main" + @echo "... test6" + @echo "... test2" + @echo "... test7" + @echo "... test" + @echo "... edit_cache" + @echo "... rebuild_cache" + @echo "... list_install_components" + @echo "... install" + @echo "... __/cpptcl.o" + @echo "... __/cpptcl.i" + @echo "... __/cpptcl.s" + @echo "... test1.o" + @echo "... test1.i" + @echo "... test1.s" + @echo "... test2.o" + @echo "... test2.i" + @echo "... test2.s" + @echo "... test3.o" + @echo "... test3.i" + @echo "... test3.s" + @echo "... test4.o" + @echo "... test4.i" + @echo "... test4.s" + @echo "... test5.o" + @echo "... test5.i" + @echo "... test5.s" + @echo "... test6.o" + @echo "... test6.i" + @echo "... test6.s" + @echo "... test7.o" + @echo "... test7.i" + @echo "... test7.s" + @echo "... test_main.o" + @echo "... test_main.i" + @echo "... test_main.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /home/sascha/git/cpptcl && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system -test: all - (cd build; ctest) diff --git a/test/test7.cc b/test/test7.cc index 7fe345f..ac902c5 100644 --- a/test/test7.cc +++ b/test/test7.cc @@ -52,7 +52,7 @@ void test1() { i.eval("fun1 1"); assert(false); } catch (tcl_error const &e) { - assert(e.what() == std::string("Usage: Needs a usage message.")); + assert(e.what() == std::string("too few arguments: 1 given, 2 required"));//std::string("Usage: Needs a usage message.")); } } From 7d778a377c392d755cfe8bbef14dcb4f5898235f Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Wed, 3 Nov 2021 21:19:34 +0100 Subject: [PATCH 06/10] allow a function to display its own help message --- cpptcl/cpptcl.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index 8bab248..c6e7542 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -61,6 +61,9 @@ class tcl_error : public std::runtime_error { explicit tcl_error(Tcl_Interp *interp) : std::runtime_error(Tcl_GetString(Tcl_GetObjResult(interp))) {} }; +class tcl_usage_message_printed { +}; + // call policies struct policies { @@ -1527,7 +1530,7 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int std::cerr << "\n"; } - if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0) { + if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0 && std::find(opts_, opts_ + num_gopt, "help") == opts_ + num_gopt) { if (num_gopt) { std::cerr << Tcl_GetString(argv[0]); for (int oi = 0; oi < num_gopt; ++oi) { @@ -1577,7 +1580,12 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int } } check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ || policies_.variadic_ ? 1 : 0) - num_gopt - num_opt, has_variadic_ || policies_.variadic_ ? -1 : sizeof...(Ts) - num_gopt, policies_.usage_); - do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + try { + do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + } catch (tcl_usage_message_printed & e) { + } catch (...) {//std::exception & e) { + throw; + } post_process_policies(interp, policies_, argv + ai, false); } } From fa55aa5815dd46fe93aa712120c9c170c6bc7dc7 Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Mon, 8 Nov 2021 03:47:07 +0100 Subject: [PATCH 07/10] per function argument type cache for faster method calls garbage collected objects proper lifetime management and sharing of object()'s --- cpptcl.cc | 290 ++++++++++++++++++++++-- cpptcl/cpptcl.h | 490 +++++++++++++++++++++++++++++++++++------ cpptcl/cpptcl_object.h | 31 ++- 3 files changed, 723 insertions(+), 88 deletions(-) diff --git a/cpptcl.cc b/cpptcl.cc index 81c60cf..124e352 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -25,6 +25,18 @@ struct constructor_handler_client_data_t { shared_ptr chb; }; +struct method_handler_client_data { + void * obj; + class_handler_base * chb; + bool unroll; + Tcl_Interp * interp; +}; + +struct managed_method_handler_client_data { + void * obj; + object_cmd_base * cb; +}; + result::result(Tcl_Interp *interp) : interp_(interp) {} result::operator bool() const { return tcl_cast::from(interp_, Tcl_GetObjResult(interp_)); } @@ -37,6 +49,10 @@ result::operator string() const { } result::operator object() const { return object(Tcl_GetObjResult(interp_)); } +void result::reset() { + Tcl_ResetResult(interp_); +} + void details::set_result(Tcl_Interp *interp, bool b ) { Tcl_SetObjResult(interp, Tcl_NewBooleanObj(b)); } void details::set_result(Tcl_Interp *interp, int i ) { Tcl_SetObjResult(interp, Tcl_NewIntObj(i)); } void details::set_result(Tcl_Interp *interp, long i ) { Tcl_SetObjResult(interp, Tcl_NewLongObj(i)); } @@ -131,7 +147,7 @@ extern "C" int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl callback_base * cb = (callback_base *) cd; try { - cb->invoke(nullptr, interp, objc, objv); + cb->invoke(nullptr, interp, objc, objv, false); //post_process_policies(interp, cdp->second, objv, false); } catch (exception const &e) { Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); @@ -150,22 +166,27 @@ extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_O // which is responsible for managing commands for // objects of a given type - class_handler_base *chb = reinterpret_cast(cd); + auto cdd = reinterpret_cast(cd); + + //class_handler_base *chb = reinterpret_cast(cd); // the command name has the form 'pXXX' where XXX is the address // of the "this" object +#if 0 string const str(Tcl_GetString(objv[0])); istringstream ss(str); char dummy; void *p; ss >> dummy >> p; - +#endif + try { //string methodName(Tcl_GetString(objv[1])); //policies &pol = chb->get_policies(methodName); - - chb->invoke(p, interp, objc, objv); + + cdd->chb->invoke(cdd->obj, interp, objc, objv, false); + //chb->invoke(p, interp, objc, objv); //post_process_policies(interp, pol, objv, true); } catch (exception const &e) { @@ -179,6 +200,32 @@ extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_O return TCL_OK; } +extern "C" int managed_method_handler(ClientData cd, Tcl_Interp * interp, int objc, Tcl_Obj *CONST objv[]) { + auto cdd = reinterpret_cast(cd); + + try { + cdd->cb->invoke(cdd->obj, interp, objc, objv, true); + } catch (std::exception & e) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); + return TCL_ERROR; + } catch (...) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown error.", -1)); + return TCL_ERROR; + } + + return TCL_OK; +} + +static void method_handler_client_data_delete(ClientData cd) { + auto * p = reinterpret_cast(cd); + if (p->unroll) { + std::ostringstream oss; + oss << 'p' << p->obj; + p->chb->uninstall_methods(p->interp, oss.str().c_str()); + } + delete p; +} + // generic "constructor" command extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { // here, client data points to the singleton object @@ -188,13 +235,111 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, constructor_handler_client_data_t * up = (constructor_handler_client_data_t *) cd; try { - up->cb->invoke(nullptr, interp, objc, objv); + up->cb->invoke(nullptr, interp, objc, objv, false); + + // if everything went OK, the result is the address of the + // new object in the 'pXXX' form + // - we can create a new command with this name + + // convert it back to a pointer for faster method calls + // object creation could be sped up by getting the raw pointer across + string const str(Tcl_GetString(Tcl_GetObjResult(interp))); + istringstream ss(str); + char dummy; + void *p; + ss >> dummy >> p; + + method_handler_client_data * cd = new method_handler_client_data; + cd->obj = p; + cd->chb = up->chb.get(); + cd->unroll = false; + cd->interp = nullptr; + + //Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(up->chb.get()), 0); + Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(cd), method_handler_client_data_delete); + } catch (exception const &e) { + Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE); + return TCL_ERROR; + } catch (...) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("Unknown error.", -1)); + return TCL_ERROR; + } + + return TCL_OK; +} + +extern "C" int managed_constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + // here, client data points to the singleton object + // which is responsible for managing commands for + // objects of a given type + + constructor_handler_client_data_t * up = (constructor_handler_client_data_t *) cd; + + try { + bool unroll = false; + bool nocommand = false; + + Tcl_Obj ** objv2 = nullptr; + if (objc > 1) { + const char * arg1 = Tcl_GetString(objv[1]); + + if (strcmp(arg1, "-methods") == 0) { + unroll = true; + } else if (strcmp(arg1, "-nocommand") == 0) { + nocommand = true; + } + + if ((nocommand || unroll)) { + if (objc > 2) { + objv2 = new Tcl_Obj *[objc - 1]; + objv2[0] = objv[0]; + for (int j = 2; j < objc; ++j) { + objv2[j - 1] = objv[j]; + } + } + --objc; + } + } + + if (objv2) { + up->cb->invoke(nullptr, interp, objc, objv2, false); + delete[] objv2; + } else { + up->cb->invoke(nullptr, interp, objc, objv, false); + } + //std::cerr << " managed construct " << Tcl_GetObjResult(interp)->refCount << "\n"; + // if everything went OK, the result is the address of the // new object in the 'pXXX' form // - we can create a new command with this name - Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(up->chb.get()), 0); + string const str(Tcl_GetString(Tcl_GetObjResult(interp))); + + istringstream ss(str); + char dummy; + void *p; + ss >> dummy >> p; + + method_handler_client_data * cd = new method_handler_client_data; + cd->obj = p; + cd->chb = up->chb.get(); + cd->unroll = unroll; + cd->interp = interp; + + //Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(up->chb.get()), 0); + + if (unroll) { + //std::cerr << "install methods\n"; + up->chb->install_methods(interp, Tcl_GetString(Tcl_GetObjResult(interp)), p); + } + + if (! nocommand) { + std::string cmd = Tcl_GetString(Tcl_GetObjResult(interp)); + cmd += "."; + Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(cd), method_handler_client_data_delete); + //std::cerr << " managed construct2 " << Tcl_GetObjResult(interp)->refCount << "\n"; + } } catch (exception const &e) { Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE); return TCL_ERROR; @@ -252,7 +397,45 @@ class_handler_base::class_handler_base() { //policies_["-delete"] = policies(); } -void class_handler_base::register_method(string const & name, shared_ptr ocb) { +static void managed_method_client_data_delete(ClientData cd) { + auto * p = reinterpret_cast(cd); + delete[] p; +} + +void class_handler_base::uninstall_methods(Tcl_Interp * interp, const char * prefix) { + char buf[1024]; + strcpy(buf, prefix); + char * p = buf + strlen(buf); + *p = '.'; ++p; + + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + strcpy(p, it->first.c_str()); + Tcl_DeleteCommand(interp, buf); + //throw tcl_error(std::string("cannot register method ") + buf + " on object creation of managed class"); + //} + } +} + +void class_handler_base::install_methods(Tcl_Interp * interp, const char * prefix, void * obj) { + char buf[1024]; + strcpy(buf, prefix); + char * p = buf + strlen(buf); + *p = '.'; ++p; + + managed_method_handler_client_data * cds = new managed_method_handler_client_data[methods_.size()]; + int ix = 0; + for (auto it = methods_.begin(); it != methods_.end(); ++it, ++ix) { + strcpy(p, it->first.c_str()); + cds[ix].obj = obj; + cds[ix].cb = it->second.get(); + //std::cerr << "install method " << buf << "\n"; + Tcl_CreateObjCommand(interp, buf, managed_method_handler, static_cast(cds + ix), ix == 0 ? managed_method_client_data_delete : nullptr); + //throw tcl_error(std::string("cannot register method ") + buf + " on object creation of managed class"); + //} + } +} + +void class_handler_base::register_method(string const & name, shared_ptr ocb, Tcl_Interp * interp, bool managed) { methods_[name] = ocb; //policies_[name] = p; } @@ -268,8 +451,11 @@ policies &class_handler_base::get_policies(string const &name) { } #endif +Tcl_Obj * object::default_object_ = nullptr; + object::object() : interp_(0) { - obj_ = Tcl_NewObj(); + if (! default_object_) { static_initialize(); } + obj_ = default_object_; Tcl_IncrRefCount(obj_); } @@ -313,7 +499,7 @@ object::object(Tcl_Obj * o, bool shared) : interp_(0) { init(o, shared); } object::object(object const & other, bool shared) : interp_(other.get_interp()) { init(other.obj_, shared); } void object::init(Tcl_Obj * o, bool shared) { - if (shared) { + if (true || shared) { obj_ = o; } else { obj_ = Tcl_DuplicateObj(o); @@ -324,42 +510,85 @@ void object::init(Tcl_Obj * o, bool shared) { object::~object() { Tcl_DecrRefCount(obj_); } object &object::assign(bool b) { - Tcl_SetBooleanObj(obj_, b); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewBooleanObj(b ? 1 : 0); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetBooleanObj(obj_, b ? 1 : 0); + } return *this; } object &object::resize(size_t size) { + dupshared(); Tcl_SetByteArrayLength(obj_, static_cast(size)); return *this; } object &object::assign(char const *buf, size_t size) { - Tcl_SetByteArrayObj(obj_, reinterpret_cast(buf), static_cast(size)); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewByteArrayObj(reinterpret_cast(buf), static_cast(size)); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetByteArrayObj(obj_, reinterpret_cast(buf), static_cast(size)); + } return *this; } object &object::assign(double d) { - Tcl_SetDoubleObj(obj_, d); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewDoubleObj(d); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetDoubleObj(obj_, d); + } return *this; } object &object::assign(int i) { - Tcl_SetIntObj(obj_, i); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewIntObj(i); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetIntObj(obj_, i); + } return *this; } object &object::assign(long l) { - Tcl_SetLongObj(obj_, l); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewLongObj(l); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetLongObj(obj_, l); + } return *this; } object &object::assign(char const *s) { - Tcl_SetStringObj(obj_, s, -1); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewStringObj(s, -1); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetStringObj(obj_, s, -1); + } return *this; } object &object::assign(string const &s) { - Tcl_SetStringObj(obj_, s.data(), static_cast(s.size())); + if (Tcl_IsShared(obj_)) { + Tcl_DecrRefCount(obj_); + obj_ = Tcl_NewStringObj(s.data(), static_cast(s.size())); + Tcl_IncrRefCount(obj_); + } else { + Tcl_SetStringObj(obj_, s.data(), static_cast(s.size())); + } return *this; } @@ -373,6 +602,11 @@ object &object::assign(Tcl_Obj *o) { return *this; } +object object::duplicate() const { + Tcl_Obj * to = Tcl_DuplicateObj(obj_); + return object(to); +} + object &object::swap(object &other) { std::swap(obj_, other.obj_); std::swap(interp_, other.interp_); @@ -442,6 +676,8 @@ object object::at(size_t index, interpreter &i) const { } object &object::append(object const &o, interpreter &i) { + dupshared(); + if (Tcl_ListObjAppendElement(i.get(), obj_, o.obj_) != TCL_OK) { throw tcl_error(i.get()); } @@ -449,6 +685,8 @@ object &object::append(object const &o, interpreter &i) { } object &object::append_list(object const &o, interpreter &i) { + dupshared(); + if (Tcl_ListObjAppendList(i.get(), obj_, o.obj_) != TCL_OK) { throw tcl_error(i.get()); } @@ -456,11 +694,11 @@ object &object::append_list(object const &o, interpreter &i) { } object &object::replace(size_t index, size_t count, object const &o, interpreter &i) { + dupshared(); int res = Tcl_ListObjReplace(i.get(), obj_, static_cast(index), static_cast(count), 1, &(o.obj_)); if (res != TCL_OK) { throw tcl_error(i.get()); } - return *this; } @@ -472,7 +710,7 @@ object &object::replace_list(size_t index, size_t count, object const &o, interp if (res != TCL_OK) { throw tcl_error(i.get()); } - + dupshared(); res = Tcl_ListObjReplace(i.get(), obj_, static_cast(index), static_cast(count), objc, objv); if (res != TCL_OK) { throw tcl_error(i.get()); @@ -487,6 +725,10 @@ Tcl_Interp *object::get_interp() const { return interp_; } Tcl::interpreter *interpreter::defaultInterpreter = nullptr; +void interpreter::unsetVar(std::string const & name) { + Tcl_UnsetVar(get_interp(), name.c_str(), 0); +} + interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { interp_ = Tcl_CreateInterp(); owner_ = true; @@ -629,6 +871,16 @@ void interpreter::add_constructor(string const &name, shared_ptr chb, callback_base * cb, policies const &p) { + constructor_handler_client_data_t * up = new constructor_handler_client_data_t; + up->cb = cb; + up->chb = chb; + + Tcl_CreateObjCommand(interp_, name.c_str(), managed_constructor_handler, up, 0); + all_definitions[interp_][name] = cb; +} + + int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { int res; if (Tcl_GetIntFromObj(interp, obj, &res) != TCL_OK) { diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index c6e7542..6ec1d89 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -157,7 +157,7 @@ class result { operator long() const; operator std::string() const; operator object() const; - + void reset(); private: Tcl_Interp *interp_; }; @@ -182,7 +182,7 @@ class callback_base { public: virtual ~callback_base() {} - virtual void invoke(void * dummyp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) = 0; + virtual void invoke(void * dummyp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; }; // base class for object command handlers @@ -191,7 +191,7 @@ 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[]) = 0; + virtual void invoke(void *p, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; }; // base class for all class handlers, still abstract @@ -201,11 +201,13 @@ class class_handler_base : public object_cmd_base { class_handler_base(); - void register_method(std::string const &name, std::shared_ptr ocb); + void register_method(std::string const &name, std::shared_ptr ocb, Tcl_Interp * interp, bool managed); //policies &get_policies(std::string const &name); - protected: + void install_methods(Tcl_Interp * interp, const char * prefix, void * obj); + void uninstall_methods(Tcl_Interp * interp, const char * prefix); +protected: typedef std::map > method_map_type; // a map of methods for the given class @@ -217,7 +219,7 @@ class class_handler_base : public object_cmd_base { // 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[]) { + virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) { C *p = static_cast(pv); //if (objc < 2) { @@ -225,8 +227,7 @@ template class class_handler : public class_handler_base { //} std::string methodName(Tcl_GetString(objv[1])); - - if (methodName == "-delete") { + if (objc == 2 && methodName == "-delete") { Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); delete p; return; @@ -239,7 +240,7 @@ template class class_handler : public class_handler_base { throw tcl_error("Method " + methodName + " not found."); } - it->second->invoke(pv, interp, objc, objv); + it->second->invoke(pv, interp, objc, objv, false); } }; @@ -300,13 +301,13 @@ template struct list { interpreter * interp_; Tcl_Obj * lo_; - Tcl_ObjType * ot_; + Tcl_ObjType ** ot_; bool list_owner_ = false; static const bool isany = details::is_any::value; list() : interp_(nullptr) { } - list(interpreter * interp, Tcl_Obj * lo, Tcl_ObjType * ot, bool list_owner = false) : interp_(interp), lo_(lo), ot_(ot), list_owner_(list_owner) { + list(interpreter * interp, Tcl_Obj * lo, Tcl_ObjType ** ot, bool list_owner = false) : interp_(interp), lo_(lo), ot_(ot), list_owner_(list_owner) { if (list_owner) { Tcl_IncrRefCount(lo); } @@ -488,19 +489,20 @@ struct any : public details::any_impl { typedef details::any_impl super_t; any() { super_t::which_ = -1; } interpreter * interp_; + Tcl_ObjType ** ot_; bool list_owner_ = false; - any(interpreter * i, Tcl_Obj * o, bool list_owner = false) : interp_(i), list_owner_(list_owner) { + any(interpreter * i, Tcl_Obj * o, Tcl_ObjType ** ot, bool list_owner = false) : interp_(i), ot_(ot), list_owner_(list_owner) { //this->p_ = o->internalRep.otherValuePtr; this->o_ = o; if (o) { - super_t::set_which(i, o); + //super_t::set_which(i, o); } if (list_owner) { Tcl_IncrRefCount(this->o_); } } - any(any const & o) : interp_(o.interp_), list_owner_(o.list_owner_) { + any(any const & o) : interp_(o.interp_), ot_(o.ot_), list_owner_(o.list_owner_) { this->o_ = o.o_; this->which_ = o.which_; if (list_owner_) { @@ -514,7 +516,11 @@ struct any : public details::any_impl { } operator bool() const { - return super_t::which_ != -1; + for (int i = 0; i < sizeof...(Ts); ++i) { + if (ot_[i] == this->o_->typePtr) return true; + } + return false; + //return super_t::which_ != -1; } template @@ -657,6 +663,9 @@ struct visit_impl { visit_impl::invoke(a, fs...); } }; +inline std::string tcl_typename(Tcl_Obj * o) { + return o->typePtr ? o->typePtr->name : "()"; +} } template @@ -691,6 +700,111 @@ struct generate_hasarg { } }; + +template +struct no_type { }; + +template +struct signature { }; + +template +struct subtype_notype_tag { + static const int value = 0; +}; +template +struct subtype_notype_tag > { + static const int value = I; +}; + +template +struct subtype { + typedef no_type type; +}; +template +struct subtype<0, T, false> { + typedef T type; +}; + +template +struct subtype, true> { + typedef typename subtype::type type; +}; +template +struct subtype<0, list, false> { + typedef typename subtype<0, T>::type type; +}; + +template +struct subtype, true> { + typedef typename subtype::type t1; + + typedef typename std::conditional< + sizeof...(Ts) && bool(subtype_notype_tag::value), + typename subtype::value - 1, any >::type, + typename subtype::type + >::type type; +}; +template +struct subtype<0, any, false> { + typedef typename subtype<0, T>::type t1; + + typedef typename std::conditional< + sizeof...(Ts) && bool(subtype_notype_tag::value), + typename subtype::value, any >::type, + typename subtype<0, T>::type + >::type type; +}; + +template +struct subtype, true> { + typedef typename subtype::type t1; + + typedef typename std::conditional< + bool(subtype_notype_tag::value), + typename subtype::value, signature >::type, + typename subtype::type + >::type type; +}; + +template +struct subtype, true> { + typedef typename subtype::type type; +}; +template +struct subtype, true > { + typedef typename subtype::type type; +}; +template +struct subtype, true> { + typedef typename subtype::type type; +}; + +template +struct subtype<0, getopt, false> { + typedef typename subtype<0, T>::type type; +}; +template +struct subtype<0, opt, false> { + typedef typename subtype<0, T>::type type; +}; +template +struct subtype<0, variadic, false> { + typedef typename subtype<0, T>::type type; +}; + + +template +struct num_subtypes { + static const int value = 1 + 100000 - subtype_notype_tag::type>::value; +}; + +template +struct generate_argtypes { + static void invoke(interpreter *, Tcl_ObjType **) { } + static const int length = 0; +}; + + template struct fix_variadic_return { typedef typename std::decay::type type; @@ -762,28 +876,37 @@ template class callbac static const int arg_offset = -num_opt; + Tcl_ObjType * argument_types_all_[generate_argtypes<0, Ts...>::length + 1]; + interpreter * interpreter_; public : - callback_v(interpreter * i, functor_type f, policies const & pol) : interpreter_(i), policies_(pol), f_(f) { + callback_v(interpreter * i, functor_type f, std::string const & name, policies const & pol) : interpreter_(i), policies_(pol), f_(f) { if (num_opt) { if (pol.options_.empty()) { - throw tcl_error("no getopt string supplied for function taking getopt<> arguments"); + throw tcl_error(std::string("no getopt string supplied for function \"") + name + "\" taking getopt<> arguments"); } generate_hasarg<0, Ts...>::invoke(has_arg_, opts_, pol.options_.c_str()); } + //std::cerr << "genargtype\n"; + generate_argtypes<0, Ts...>::invoke(i, argument_types_all_); } template struct void_return { }; + template + std::pair args() { + return generate_argtypes<0, Ts...>::template get(argument_types_all_); + } + // regular function (return / void) template ::value, bool>::type = true> void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) { if (argc && argv) { } - details::set_result(interp, f_(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); + details::set_result(interp, f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); } template ::value, bool>::type = true> void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { if (argc && argv && interp) { } - f_(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); + f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); } @@ -791,28 +914,28 @@ template class callbac template ::value, bool>::type = true> void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { if (argc && argv) { } - details::set_result(interp, f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); + details::set_result(interp, f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); } template ::value, bool>::type = true> void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { if (argc && argv && interp) { } - f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); + f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); } - + // method (return / void) template ::value && !std::is_same::value, bool>::type = true> void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { if (argc && argv) { } - details::set_result(interp, (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); + details::set_result(interp, (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); } template ::value && !std::is_same::value, bool>::type = true> void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { if (argc && argv && interp) { } - (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); + (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); } - - void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv []); + + void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method); }; template @@ -1035,9 +1158,18 @@ class interpreter { } bool trace_ = false; + int trace_count() const { + return trace_count_; + } + void trace_count(int v) { + trace_count_ = v; + } + + int trace_count_ = 0; + template class class_definer { public: - class_definer(std::shared_ptr> ch, interpreter * inter) : ch_(ch), interp_(inter) {} + class_definer(std::shared_ptr> ch, interpreter * inter, bool managed) : ch_(ch), interp_(inter), managed_(managed) {} template class_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { @@ -1046,17 +1178,17 @@ class interpreter { template class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p))); + ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, name, p)), interp_->get_interp(), managed_); return *this; } template class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, p))); + ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, name, p)), interp_->get_interp(), managed_); return *this; } template class_definer & def2(std::string const & name, std::function fn, policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::callback_v, std::function, R, Ts...>(interp_, fn, p))); + ch_->register_method(name, std::shared_ptr(new details::callback_v, std::function, R, Ts...>(interp_, fn, name, p)), interp_->get_interp(), managed_); return *this; } @@ -1070,6 +1202,7 @@ class interpreter { private: interpreter * interp_; std::shared_ptr> ch_; + bool managed_; }; template @@ -1101,11 +1234,11 @@ class interpreter { template void def(std::string const & name, R(*f)(Ts...), policies const & p = policies()) { - add_function(name, new details::callback_v(this, f, p)); + add_function(name, new details::callback_v(this, f, name, p)); } template void def(std::string const & name, std::function fn, policies const & p = policies()) { - add_function(name, new details::callback_v, R, Ts...>(this, fn, p)); + add_function(name, new details::callback_v, R, Ts...>(this, fn, name, p)); } template @@ -1120,7 +1253,7 @@ class interpreter { template void defvar(std::string const & name, T & v); - + template function_definer def(C * this_p) { return function_definer(this_p, this); @@ -1139,10 +1272,34 @@ class interpreter { add_class(name, ch); - add_constructor(name, ch, new callback_type(this, &call_constructor::doit, p), p); + add_constructor(name, ch, new callback_type(this, &call_constructor::doit, name, p), p); + + return class_definer(ch, this, false); + } + +#define CPPTCL_MANAGED_CLASS +#ifdef CPPTCL_MANAGED_CLASS + template struct call_managed_constructor { + interpreter * interp_; + call_managed_constructor(interpreter * i) : interp_(i) { } + + object operator()(Ts... ts); + }; + + template class_definer managed_class_(std::string const &name, init const & = init(), policies const &p = policies()) { + typedef details::callback_v, object, Ts...> callback_type; + + std::shared_ptr> ch(new details::class_handler()); + + this->type >(name, (C *) 0); + + add_class(name, ch); + + add_managed_constructor(name, ch, new callback_type(this, call_managed_constructor(this), name, p), p); - return class_definer(ch, this); + return class_definer(ch, this, true); } +#endif std::map obj_type_by_tclname_; std::map obj_type_by_cppname_; @@ -1191,7 +1348,6 @@ class interpreter { //std::cerr << "dup\n"; return new shared_ptr_deleter(p_); } - ~shared_ptr_deleter() { //std::cerr << "~shptr deleter\n"; } @@ -1201,6 +1357,7 @@ class interpreter { struct type_ops { static void dup(Tcl_Obj * src, Tcl_Obj * dst) { //2p dst->internalRep.otherValuePtr = src->internalRep.otherValuePtr; + //std::cerr << "dup " << src->typePtr << " " << (src->typePtr ? src->typePtr->name : "()" ) << "\n"; if (src->internalRep.twoPtrValue.ptr2) { //std::cerr << "dup osrc=" << src << "[" << src->refCount << "] odst=" << dst << "[" << dst->refCount << "]\n";// type=" << src->typePtr << " intern=" << src->internalRep.otherValuePtr << "\n"; deleter * d = (deleter *) src->internalRep.twoPtrValue.ptr2; @@ -1222,7 +1379,10 @@ class interpreter { } } } - static void str(Tcl_Obj *) { + static void str(Tcl_Obj * o) { + std::ostringstream oss; + oss << 'p' << o->internalRep.twoPtrValue.ptr1; + str_impl(o, oss.str()); } static void str_impl(Tcl_Obj * o, std::string const & name) { o->bytes = Tcl_Alloc(name.size() + 1); @@ -1231,12 +1391,14 @@ class interpreter { o->length = name.size(); } static int set(Tcl_Interp *, Tcl_Obj *) { + //std::cerr << "type set\n"; return TCL_OK; } }; template Tcl_ObjType * get_objtype() { + //std::cerr << "get_objtype\n"; auto it = obj_type_by_cppname_.find(std::type_index(typeid(TY))); if (it != obj_type_by_cppname_.end()) { return it->second; @@ -1244,12 +1406,14 @@ class interpreter { return nullptr; } Tcl_ObjType * get_objtype(const char * name) { + //std::cerr << "get_objtype\n"; auto & o = obj_type_by_tclname_; auto it = o.find(name); return it == o.end() ? nullptr : it->second; } template bool is_type(Tcl_Obj * o) { + //std::cerr << "is_type\n"; return o->typePtr && get_objtype() == o->typePtr; if (o->typePtr) { std::type_index * tip = (std::type_index *) (o->typePtr->name + 256); @@ -1261,6 +1425,7 @@ class interpreter { } template T * try_type(Tcl_Obj * o) { + //std::cerr << "try_type\n"; if (is_type(o)) { //2p return (T *) o->internalRep.otherValuePtr; return (T *) o->internalRep.twoPtrValue.ptr1; @@ -1300,17 +1465,41 @@ class interpreter { T tcl_cast(Tcl_Interp *, Tcl_Obj *, bool) { throw; } + template ::type>::value, bool>::type = true> + T tcl_cast(Tcl_Interp *, Tcl_Obj *, bool, Tcl_ObjType *) { + throw; + } + template T tcl_cast(Tcl_Obj * o) { return this->template tcl_cast(this->get_interp(), o, false); } + template + T tcl_cast(Tcl_Obj * o, Tcl_ObjType * ot) { + return this->template tcl_cast(this->get_interp(), o, false, ot); + } template ::value, bool>::type = true> T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { + auto it = obj_type_by_cppname_.find(std::type_index(typeid(typename std::remove_pointer::type))); + if (it != obj_type_by_cppname_.end()) { + //std::cerr << "castingold...\n"; + return this->template tcl_cast(i, o, byref, it->second); + } + throw tcl_error("function argument type is not registered"); + } + + template ::value, bool>::type = true> + T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref, Tcl_ObjType * ot) { if (std::is_pointer::value && o->typePtr) { - auto it = obj_type_by_cppname_.find(std::type_index(typeid(typename std::remove_pointer::type))); - if (it != obj_type_by_cppname_.end()) { - if (it->second == o->typePtr) { + //auto it = obj_type_by_cppname_.find(std::type_index(typeid(typename std::remove_pointer::type))); + //if (it != obj_type_by_cppname_.end()) { + if (true) { + // Tcl_ObjType * otp = it->second; + Tcl_ObjType * otp = ot; + //std::cerr << "casting... " << otp << " " << o->typePtr << " " << o->typePtr->name << "\n"; + + if (otp == o->typePtr) { //return (typename std::conditional::value, T, T*>::type) o->internalRep.otherValuePtr; //std::cerr << "objcast " << o << " " << o->internalRep.otherValuePtr << "\n"; //return (T) o->internalRep.otherValuePtr; @@ -1321,7 +1510,7 @@ class interpreter { if (len == 1) { Tcl_Obj *o2; if (Tcl_ListObjIndex(i, o, 0, &o2) == TCL_OK) { - if (it->second == o2->typePtr) { + if (otp == o2->typePtr) { //std::cerr << "objcast(list=len1) " << o2 << " " << o2->internalRep.otherValuePtr << "\n"; //2p return (T) o2->internalRep.otherValuePtr; return (T) o2->internalRep.twoPtrValue.ptr1; @@ -1350,21 +1539,38 @@ class interpreter { T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { return details::tcl_cast::from(i, o, byref); } + template ::value && std::is_same::type>::value && !details::is_any::value, bool>::type = true> + T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref, Tcl_ObjType *) { + return details::tcl_cast::from(i, o, byref); + } template 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 class_definer(ch, this); + return class_definer(ch, this, false); + } + + template class_definer managed_class_(std::string const &name, details::no_init_type const &) { + std::shared_ptr> ch(new details::class_handler()); + + add_class(name, ch); + + return class_definer(ch, this, true); } + // free script evaluation details::result eval(std::string const &script); details::result eval(std::istream &s); details::result eval(object const &o); + void result_reset() { + Tcl_ResetResult(get_interp()); + } + // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result eval(InputIterator first, InputIterator last); @@ -1373,12 +1579,18 @@ class interpreter { object makeobj(OT * n, bool owning = false, DEL delfn = []{}); template object makeobj(OT * n, bool owning = false); - + template + void makeobj_inplace(OT * p, object & obj, bool owning = false, DEL delfn = []{}); + template void setVar(std::string const & name, T const & value); + void setVar(std::string const & name, object const & value); + template void setVar(std::string const & name, object & ptr); + + void unsetVar(std::string const & name); // Get a variable from TCL interpreter with Tcl_GetVar details::result getVar(std::string const &scalarTclVariable); @@ -1408,6 +1620,7 @@ class interpreter { void add_class(std::string const &name, std::shared_ptr chb); void add_constructor(std::string const &name, std::shared_ptr chb, details::callback_base * cb, policies const &p = policies()); + void add_managed_constructor(std::string const &name, std::shared_ptr chb, details::callback_base * cb, policies const &p = policies()); Tcl_Interp *interp_; bool owner_; @@ -1432,15 +1645,15 @@ typename list::return_t list::at(std::size_t ix) const { Tcl_Obj *o; if (Tcl_ListObjIndex(interp_->get(), lo_, ix, &o) == TCL_OK) { if constexpr (isany) { - return return_t(interp_, o); + return return_t(interp_, o, ot_); } else if constexpr (details::is_basic_type::value) { return interp_->template tcl_cast(o); } else { - if (o->typePtr == ot_) { + if (o->typePtr == ot_[0]) { //return (return_t) o->internalRep.otherValuePtr; return (return_t) o->internalRep.twoPtrValue.ptr1; } else { - throw tcl_error("Unexpected type in list"); + throw tcl_error("Unexpected type in list. Got " + details::tcl_typename(o) + " need " + ot_[0]->name); } } } @@ -1451,6 +1664,7 @@ template template typename std::conditional::value, TT, TT *>::type any::as() const { //if (this->interp_->try_type(this->o_->typePtr)) { +#if 0 if (this->which_ == details::type_at::value) { if constexpr (details::is_list::value) { if (this->o_->typePtr && strcmp(this->o_->typePtr->name, "list") == 0) { @@ -1469,9 +1683,79 @@ typename std::conditional::value, TT, TT *>::type any::value; + + if constexpr (details::is_list::value) { + if (this->o_ && this->o_->typePtr && strcmp(this->o_->typePtr->name, "list") == 0) { + Tcl_Obj * oo; + if (Tcl_ListObjIndex(interp_->get_interp(), this->o_, 0, &oo) == TCL_OK) { +#if 0 + std::cerr << sizeof...(Ts) << " " << details::type_at::value << " " << details::tcl_typename(oo) << "\n"; + for (int i = 0; i < sizeof...(Ts); ++i) { + std::cerr << i << " " << this->ot_[i]->name << "\n"; + } +#endif + if (oo->typePtr == this->ot_[toff]) { + return TT(interp_, this->o_, this->ot_ + toff); + } + } + return TT(); + } else { + return TT(); + //throw tcl_error(std::string("bad cast of ") + (this->o_->typePtr ? this->o_->typePtr->name : "(none)") + " to list"); + } + } else { + //std::cerr << this->o_->typePtr << " " << this->o_->typePtr->name << " " << details::type_at::value << " " << this->ot_ << "\n"; + if (this->o_ && this->ot_[toff] == this->o_->typePtr) { + return (TT *) this->o_->internalRep.twoPtrValue.ptr1; + } else { + return nullptr; + } + } +#endif } namespace details { +template +struct generate_argtypes { + template + struct wrap { }; + + typedef typename remove_rc::type T; + + template + static Tcl_ObjType * lookup_type(interpreter * interp, int pos, wrap * np) { + auto * ot = interp->get_objtype::type>(); + //std::cerr << "lookup " << pos << " " << "_Z" << typeid(TT).name() << " " << ot << " " << (ot ? ot->name : "()") << "\n"; + return ot; + //return interp->get_objtype(); + } + template + static void invoke_helper(std::index_sequence const &, interpreter * interp, Tcl_ObjType ** all) { + ((all[Is] = lookup_type(interp, Is, (wrap::type> *) 0)), ...); + //p[0] = all; + //p[1] = all + sizeof...(is); + generate_argtypes::invoke(interp, all + sizeof...(Is)); + } + static void invoke(interpreter * interp, Tcl_ObjType ** all) { + //std::cerr << "gat _Z" << typeid(T).name() << " " << num_subtypes::value << "\n"; + invoke_helper(std::make_index_sequence::value>(), interp, all); + } + + template + static std::pair get(Tcl_ObjType ** p) { + if constexpr (ArgI == I) { + return { p, p + num_subtypes::value }; + } else { + return generate_argtypes::template get(p + num_subtypes::value); + } + } + + static const int length = num_subtypes::value + generate_argtypes::length; +}; + + template inline void any_impl::set_which(interpreter * i, Tcl_Obj * o) { if constexpr (is_list::value) { @@ -1510,12 +1794,12 @@ struct back { }; template -void callback_v::invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv []) { +void callback_v::invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { static const int num_gopt = num_getopt::value; static const int num_opt = num_optional::value; Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; Tcl_Obj boolean_dummy_object; - std::size_t ai = std::is_same::value ? 1 : 2; + std::size_t ai = std::is_same::value || object_dot_method ? 1 : 2; static const bool has_variadic_ = has_variadic::value; if (interpreter_->trace()) { @@ -1529,6 +1813,7 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int } std::cerr << "\n"; } + ++interpreter_->trace_count_; if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0 && std::find(opts_, opts_ + num_gopt, "help") == opts_ + num_gopt) { if (num_gopt) { @@ -1547,7 +1832,7 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int if (num_gopt) { for (; ai < argc; ++ai) { - if (argv[ai]->typePtr) break; + //if (argv[ai]->typePtr) break; char * s = Tcl_GetString(argv[ai]); //std::cerr << "look at: " << s << "\n"; if (s[0] == '-') { @@ -1583,6 +1868,17 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int try { do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); } catch (tcl_usage_message_printed & e) { + } catch (std::exception & e) { + for (int i = 0; i < argc; ++i) { + if (i) { std::cerr << " "; } + if (argv[i]->typePtr && strcmp(argv[i]->typePtr->name, "list") == 0) { + std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; + } else { + std::cerr << Tcl_GetString(argv[i]); + } + } + std::cerr << ": " << e.what() << "\n"; + return; } catch (...) {//std::exception & e) { throw; } @@ -1592,10 +1888,29 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int #include "cpptcl/cpptcl_object.h" +#ifdef CPPTCL_MANAGED_CLASS +template +object interpreter::call_managed_constructor::operator()(Ts... ts) { + object ret = interp_->makeobj(new C(ts...), true, [this](C * p) { + std::ostringstream oss; + oss << "p" << p << "."; + Tcl_DeleteCommand(interp_->get_interp(), oss.str().c_str()); + //std::cerr << oss.str() << " removed\n"; + delete p; + }); + if (false) { + std::cerr << "managed_create " << ret.get_object() + << " " << ret.get_object()->internalRep.twoPtrValue.ptr1 + << " " << ret.get_object()->internalRep.twoPtrValue.ptr2 + << "\n"; + } + return ret; +} +#endif namespace details { template -typename details::fix_variadic_return::type do_cast(interpreter * tcli, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol) { +typename details::fix_variadic_return::type do_cast(interpreter * tcli, std::pair objecttypes, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol) { typedef typename remove_rc::type TT; static const bool is_variadic_arg = is_variadic::value; @@ -1603,7 +1918,7 @@ typename details::fix_variadic_return::type do_cast(interpr static const bool is_getopt_arg = is_getopt::value; static const bool is_old_variadic_arg = Ii + 1 == In && std::is_same::type, object const &>::value; - + typedef typename list_unpack::type list_unpack_t; static const bool is_list_arg = !std::is_same::value; static const bool cpptcl_byref = false; @@ -1631,7 +1946,7 @@ typename details::fix_variadic_return::type do_cast(interpr return TT(true, true); } else { //std::cerr << "optional\n"; - return TT(do_cast<0, opt_unpack_t, In, Ii>(tcli, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol), true); + return TT(do_cast<0, opt_unpack_t, In, Ii>(tcli, objecttypes, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol), true); //return TT(tcli->template tcl_cast(interp, getoptv[getopti], cpptcl_byref), true); } } else { @@ -1640,7 +1955,7 @@ typename details::fix_variadic_return::type do_cast(interpr } } else { if (objc > Ii + ArgOffset) { - return TT(do_cast(tcli, pack, variadic, interp, objc, objv, getoptc, getoptv, pol), true); + return TT(do_cast(tcli, objecttypes, pack, variadic, interp, objc, objv, getoptc, getoptv, pol), true); //return { tcli->template tcl_cast(interp, objv[Ii + ArgOffset], cpptcl_byref), true }; } else { return { typename std::decay::type(), false }; @@ -1650,13 +1965,15 @@ typename details::fix_variadic_return::type do_cast(interpr if (objv[Ii + ArgOffset]->typePtr) { Tcl_ObjType * ot = tcli->get_objtype(); if (strcmp(objv[Ii + ArgOffset]->typePtr->name, "list") == 0) { - return TT(tcli, objv[Ii + ArgOffset], ot); - return return_dummy_value::doit(tcli, objv[Ii + ArgOffset], ot); + return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); + //ot return TT(tcli, objv[Ii + ArgOffset], ot); + //return return_dummy_value::doit(tcli, objv[Ii + ArgOffset], ot); } else { Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); //std::cerr << "create listone " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << (ot ? ot->name : "") << "\n"; - return TT(tcli, lo, ot, true); - return return_dummy_value::doit(tcli, lo, ot); + return TT(tcli, lo, objecttypes.first, true); + //ot return TT(tcli, lo, ot, true); + //return return_dummy_value::doit(tcli, lo, ot); } } else { return TT(nullptr, nullptr, nullptr); @@ -1669,9 +1986,9 @@ typename details::fix_variadic_return::type do_cast(interpr int len; if (Tcl_ListObjLength(interp, objv[Ii + ArgOffset], &len) == TCL_OK) { if (len == 0) { - return TT(nullptr, nullptr); + return TT(nullptr, nullptr, nullptr); } else { - return TT(tcli, objv[Ii + ArgOffset]); + return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); } } else { throw tcl_error("cannot query list length"); @@ -1682,7 +1999,7 @@ typename details::fix_variadic_return::type do_cast(interpr if (len == 1) { Tcl_Obj * oo; if (Tcl_ListObjIndex(interp, objv[Ii + ArgOffset], 0, &oo) == TCL_OK) { - return TT(tcli, oo); + return TT(tcli, oo, objecttypes.first); } else throw tcl_error("cannot get list element"); } else throw tcl_error("expecting object or list of size one, got list of size " + std::to_string(len)); } else throw tcl_error("cannot get list size"); @@ -1691,15 +2008,15 @@ typename details::fix_variadic_return::type do_cast(interpr } else { if (is_any_list_arg) { Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); - return TT(tcli, lo, true); + return TT(tcli, lo, objecttypes.first, true); } else { - return TT(tcli, objv[Ii + ArgOffset]); + return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); } } //return return_dummy_value::doit(ret.set(tcli, objv[Ii + ArgOffset])); } else { if constexpr (is_any_list_arg) { - return TT(nullptr, nullptr); + return TT(nullptr, nullptr, nullptr); } else { throw tcl_error("any<> argument does not map to an object"); } @@ -1707,7 +2024,7 @@ typename details::fix_variadic_return::type do_cast(interpr //return TT(nullptr, nullptr); } } else { - if constexpr (is_variadic_arg) { // && variadic) { + if constexpr (is_variadic_arg) { //return details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol); //return details::return_dummy_value::doit(details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol)); return TT(tcli, objc, objv); @@ -1717,7 +2034,11 @@ typename details::fix_variadic_return::type do_cast(interpr //} //std::cerr << "regular arg " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << objv[Ii + ArgOffset] << " " << Ii << " " << ArgOffset << " " << objv[Ii + ArgOffset]->typePtr << "[" << (objv[Ii + ArgOffset]->typePtr ? objv[Ii + ArgOffset]->typePtr->name : "") << "]\n"; //return details::tcl_cast::from(interp, objv[Ii + ArgOffset], false); - return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false); + if constexpr (std::is_pointer::value && std::is_class::type>::value) { + return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false, *objecttypes.first); + } else { + return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false); + } } } } @@ -1753,16 +2074,42 @@ interpreter::class_definer & interpreter::class_definer::defvar(std::strin }); } +inline void interpreter::setVar(std::string const & name, object const & value) { + object name_obj(name); + +#if 0 + Tcl_Obj * ro2 = Tcl_ObjGetVar2(interp_, name_obj.get_object(), nullptr, 0); + Tcl_Obj * ro; + { + object value2 = value.duplicate(); + std::cerr << "sharedo1: " << value.shared() << " " << name_obj.shared() << " " << value.get_object()->refCount << " " << value2.get_object() << " " << ro2 << " " << (ro2 ? ro2->refCount : -22) << "\n"; + ro = Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, value2.get_object(), 0); + } + + std::cerr << "sharedo2: " << value.shared() << " " << name_obj.shared() << " " << ro << " " << ro2 << " " << value.get_object() << " " << ro->refCount << " " << value.get_object()->refCount << "\n"; +#else + Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, value.get_object(), 0); +#endif +} + template void interpreter::setVar(std::string const & name, T const & value) { object val_obj(value); object name_obj(name); + + std::cerr << "shared1: " << val_obj.shared() << " " << name_obj.shared() << "\n"; Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, val_obj.get_object(), 0); + std::cerr << "shared2: " << val_obj.shared() << " " << name_obj.shared() << "\n"; } template -object interpreter::makeobj(OT * n, bool owning, DEL delfn) { - Tcl_Obj * o = Tcl_NewObj(); +void interpreter::makeobj_inplace(OT * n, object & obj, bool owning, DEL delfn) { + Tcl_Obj * o = obj.get_object(); //Tcl_NewObj(); + //Tcl_Obj * o = Tcl_NewObj(); + + if (Tcl_IsShared(o)) { + throw tcl_error("cannot modify shared object in-place"); + } o->internalRep.twoPtrValue.ptr1 = (void *) n; if (owning) { @@ -1785,8 +2132,15 @@ object interpreter::makeobj(OT * n, bool owning, DEL delfn) { throw tcl_error("type lookup failed"); } Tcl_InvalidateStringRep(o); - return object(o, true); + //return object(o, true); } +template +object interpreter::makeobj(OT * p, bool owning, DELFN delfn) { + object obj = object().duplicate(); + makeobj_inplace(p, obj, owning, delfn); + return obj; +} + template object interpreter::makeobj(OT * n, bool owning) { return makeobj(n, owning, (void *) 0 ); diff --git a/cpptcl/cpptcl_object.h b/cpptcl/cpptcl_object.h index 582f6f3..67e99bf 100644 --- a/cpptcl/cpptcl_object.h +++ b/cpptcl/cpptcl_object.h @@ -233,7 +233,36 @@ class object { // helper function used from copy constructors void init(Tcl_Obj *o, bool shared); - public: + static Tcl_Obj * default_object_; + static void static_initialize() { + default_object_ = Tcl_NewObj(); + Tcl_IncrRefCount(default_object_); + } + void dupshared() { + if (Tcl_IsShared(obj_)) { + Tcl_Obj * newo = Tcl_DuplicateObj(obj_); + Tcl_IncrRefCount(newo); + Tcl_DecrRefCount(obj_); + obj_ = newo; + } + } + public: + static object make() { + //if (! default_object_) { + // static_initialize(); + //} + return object().duplicate(); + } + void disown() { + Tcl_DecrRefCount(obj_); + } + void reown() { + Tcl_IncrRefCount(obj_); + } + object duplicate() const; + bool shared() const { + return Tcl_IsShared(obj_); + } Tcl_Obj *obj_; Tcl_Interp *interp_; }; From c55c739d457bd7af26647146972b0b8835befa7c Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Fri, 26 Nov 2021 06:08:44 +0100 Subject: [PATCH 08/10] conversions, call policies, gd example * allow a function to be mapped with some argument conversions being applied useful for binding to existing code and interfaces * allow to specify whether a function is cold or not to use different optimization tag all constructors and destructors and register functions as cold for all functions (hot and cold) * an not yet 100% complete example GD library binding, derived and supposed to be compatible with tcl.gd. Code size reduced to 10%, as the original was very boilerplaty. --- cpptcl.cc | 192 +++++- cpptcl/cpptcl.h | 1378 ++++++++++++++++++++++++++++------------ cpptcl/cpptcl_object.h | 14 - libraries/gd.cpp | 270 ++++++++ libraries/gd.hpp | 84 +++ 5 files changed, 1485 insertions(+), 453 deletions(-) create mode 100644 libraries/gd.cpp create mode 100644 libraries/gd.hpp diff --git a/cpptcl.cc b/cpptcl.cc index 124e352..0905a16 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -13,6 +13,10 @@ #include #include +#include + +#include + #include "cpptcl/cpptcl.h" using namespace Tcl; @@ -22,7 +26,7 @@ using namespace std; typedef std::pair, policies> callback_handler_client_data_t; struct constructor_handler_client_data_t { callback_base * cb; - shared_ptr chb; + class_handler_base * chb; }; struct method_handler_client_data { @@ -34,7 +38,7 @@ struct method_handler_client_data { struct managed_method_handler_client_data { void * obj; - object_cmd_base * cb; + callback_base * cb; }; result::result(Tcl_Interp *interp) : interp_(interp) {} @@ -47,7 +51,7 @@ result::operator string() const { Tcl_Obj *obj = Tcl_GetObjResult(interp_); return Tcl_GetString(obj); } -result::operator object() const { return object(Tcl_GetObjResult(interp_)); } +result::operator object() const { object ret(Tcl_GetObjResult(interp_)); ret.set_interp(interp_); return ret; } void result::reset() { Tcl_ResetResult(interp_); @@ -60,12 +64,27 @@ void details::set_result(Tcl_Interp *interp, double d ) { Tcl_SetObjResult void details::set_result(Tcl_Interp *interp, string const &s) { Tcl_SetObjResult(interp, Tcl_NewStringObj(s.data(), static_cast(s.size()))); } void details::set_result(Tcl_Interp *interp, void *p) { ostringstream ss; - ss << 'p' << p; + ss << interpreter::object_namespace_name << "::" << p; string s(ss.str()); - - Tcl_SetObjResult(interp, Tcl_NewStringObj(s.data(), static_cast(s.size()))); + Tcl_Obj * to = Tcl_NewStringObj(s.data(), static_cast(s.size())); + to->internalRep.otherValuePtr = nullptr; + Tcl_SetObjResult(interp, to); +} +void details::set_result(Tcl_Interp *interp, named_pointer_result p) { + Tcl_Obj * to; + if (p.name.empty()) { + ostringstream ss; + ss << interpreter::object_namespace_name << "::" << p.p; + string s(ss.str()); + to = Tcl_NewStringObj(s.data(), static_cast(s.size())); + } else { + to = Tcl_NewStringObj(p.name.data(), p.name.size()); + } + to->internalRep.otherValuePtr = p.p; + Tcl_SetObjResult(interp, to); } + void details::set_result(Tcl_Interp *interp, object const &o) { Tcl_SetObjResult(interp, o.get_object()); } void details::check_params_no(int objc, int required, int maximum, const std::string &message) { @@ -117,7 +136,7 @@ typedef std::map all_definitions_t; all_definitions_t all_definitions; // map of object handlers -typedef map> class_interp_map; +typedef map class_interp_map; typedef map class_handlers_map; class_handlers_map class_handlers; @@ -141,6 +160,7 @@ extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_O // actual functions handling various callbacks // generic callback handler +#if 0 extern "C" int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { // callback_handler_client_data_t * cdp = (callback_handler_client_data_t *) cd; @@ -159,7 +179,8 @@ extern "C" int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl return TCL_OK; } - +#endif + // generic "object" command handler extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { // here, client data points to the singleton object @@ -243,19 +264,18 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, // convert it back to a pointer for faster method calls // object creation could be sped up by getting the raw pointer across - string const str(Tcl_GetString(Tcl_GetObjResult(interp))); + string const str(Tcl_GetString(Tcl_GetObjResult(interp)) + interpreter::object_namespace_prefix_len); istringstream ss(str); - char dummy; void *p; - ss >> dummy >> p; + ss >> p; method_handler_client_data * cd = new method_handler_client_data; cd->obj = p; - cd->chb = up->chb.get(); + cd->chb = up->chb; cd->unroll = false; cd->interp = nullptr; - //Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(up->chb.get()), 0); + //Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(up->chb), 0); Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(cd), method_handler_client_data_delete); } catch (exception const &e) { Tcl_SetResult(interp, const_cast(e.what()), TCL_VOLATILE); @@ -314,20 +334,19 @@ extern "C" int managed_constructor_handler(ClientData cd, Tcl_Interp *interp, in // new object in the 'pXXX' form // - we can create a new command with this name - string const str(Tcl_GetString(Tcl_GetObjResult(interp))); + string const str(Tcl_GetString(Tcl_GetObjResult(interp)) + interpreter::object_namespace_prefix_len); istringstream ss(str); - char dummy; void *p; - ss >> dummy >> p; + ss >> p; method_handler_client_data * cd = new method_handler_client_data; cd->obj = p; - cd->chb = up->chb.get(); + cd->chb = up->chb; cd->unroll = unroll; cd->interp = interp; - //Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(up->chb.get()), 0); + //Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(up->chb), 0); if (unroll) { //std::cerr << "install methods\n"; @@ -337,7 +356,18 @@ extern "C" int managed_constructor_handler(ClientData cd, Tcl_Interp *interp, in if (! nocommand) { std::string cmd = Tcl_GetString(Tcl_GetObjResult(interp)); cmd += "."; - Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(cd), method_handler_client_data_delete); + //std::cerr << "ohandler " << (void *) object_handler << " " << (void *) cd << "\n"; + //std::cerr << "create command " << cmd << "\n"; + if (! Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(cd), method_handler_client_data_delete)) { + std::cerr << "cannot create command " << cmd << "\n"; + } + + //Tcl_CmdInfo cmdinfo; + + //Tcl_GetCommandInfo(interp, cmd.c_str(), &cmdinfo); + + //std::cerr << "cmdinfo proc=" << (void *) cmdinfo.objProc << " cd=" << cmdinfo.objClientData << " ns=" << cmdinfo.namespacePtr << "\n"; + //std::cerr << " managed construct2 " << Tcl_GetObjResult(interp)->refCount << "\n"; } } catch (exception const &e) { @@ -409,7 +439,7 @@ void class_handler_base::uninstall_methods(Tcl_Interp * interp, const char * pre *p = '.'; ++p; for (auto it = methods_.begin(); it != methods_.end(); ++it) { - strcpy(p, it->first.c_str()); + strcpy(p, it->first); Tcl_DeleteCommand(interp, buf); //throw tcl_error(std::string("cannot register method ") + buf + " on object creation of managed class"); //} @@ -425,9 +455,9 @@ void class_handler_base::install_methods(Tcl_Interp * interp, const char * prefi managed_method_handler_client_data * cds = new managed_method_handler_client_data[methods_.size()]; int ix = 0; for (auto it = methods_.begin(); it != methods_.end(); ++it, ++ix) { - strcpy(p, it->first.c_str()); + strcpy(p, it->first); cds[ix].obj = obj; - cds[ix].cb = it->second.get(); + cds[ix].cb = it->second; //std::cerr << "install method " << buf << "\n"; Tcl_CreateObjCommand(interp, buf, managed_method_handler, static_cast(cds + ix), ix == 0 ? managed_method_client_data_delete : nullptr); //throw tcl_error(std::string("cannot register method ") + buf + " on object creation of managed class"); @@ -435,8 +465,10 @@ void class_handler_base::install_methods(Tcl_Interp * interp, const char * prefi } } -void class_handler_base::register_method(string const & name, shared_ptr ocb, Tcl_Interp * interp, bool managed) { - methods_[name] = ocb; +void class_handler_base::register_method(string const & name, callback_base * ocb, Tcl_Interp * interp, bool managed) { + char * str = new char[name.size() + 1]; + strcpy(str, name.c_str()); + methods_[str] = ocb; //policies_[name] = p; } @@ -735,6 +767,7 @@ interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { if (defaultInterpreter) { throw tcl_error("expecting a single interpreter"); } + find_standard_types(); } interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_(nullptr), terr_(nullptr) { @@ -745,11 +778,40 @@ interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_( throw tcl_error("Failed to initialize stubs"); } // Make a copy + } + find_standard_types(); + if (!defaultInterpreter) { defaultInterpreter = new interpreter(*this); } } -interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.owner_), tin_(nullptr), tout_(nullptr), terr_(nullptr) {} +interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.owner_), tin_(nullptr), tout_(nullptr), terr_(nullptr), + list_type_(i.list_type_), cmdname_type_(i.cmdname_type_), object_namespace_(i.object_namespace_) { +} + +void interpreter::custom_construct(const char * c_name, const char * o_name, void * p) { + auto it = class_handlers.find(interp_); + if (it != class_handlers.end()) { + auto it2 = it->second.find(c_name); + if (it2 != it->second.end()) { + auto * cd = new method_handler_client_data; + cd->obj = p; + cd->chb = it2->second; + cd->unroll = false; + cd->interp = nullptr; + Tcl_CreateObjCommand(interp_, o_name, object_handler, static_cast(cd), method_handler_client_data_delete); + return; + } + } + throw tcl_error("custom construct failed"); +} + +void interpreter::find_standard_types() { + list_type_ = Tcl_GetObjType("list"); + cmdname_type_ = Tcl_GetObjType("cmdName"); + object_namespace_ = Tcl_FindNamespace(interp_, object_namespace_name, Tcl_GetGlobalNamespace(interp_), 0); + //object_command_namespace_ = Tcl_FindNamespace(interp_, object_command_namespace_name, nullptr, 0); +} interpreter::~interpreter() { if (owner_) { @@ -850,19 +912,37 @@ void interpreter::clear_definitions(Tcl_Interp *interp) { delete it2->second; } all_definitions.erase(it); - class_handlers.erase(interp); + + { + auto it = class_handlers.find(interp); + if (it != class_handlers.end()) { + for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { + delete it2->second; + } + } + class_handlers.erase(interp); + } } void interpreter::add_function(string const &name, callback_base * cb) { - Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, cb, 0); + //Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, cb, 0); + cb->install(); all_definitions[interp_][name] = cb; } -void interpreter::add_class(string const &name, shared_ptr chb) { +void interpreter::add_class(string const &name, class_handler_base * chb) { class_handlers[interp_][name] = chb; } +details::class_handler_base * interpreter::get_class_handler(std::string const & name) { + auto it = class_handlers.find(interp_); + if (it == class_handlers.end()) return nullptr; + auto it2 = it->second.find(name); + if (it2 == it->second.end()) return nullptr; + return it2->second; +} + -void interpreter::add_constructor(string const &name, shared_ptr chb, callback_base * cb, policies const &p) { +void interpreter::add_constructor(string const &name, class_handler_base * chb, callback_base * cb) { constructor_handler_client_data_t * up = new constructor_handler_client_data_t; up->cb = cb; up->chb = chb; @@ -871,7 +951,7 @@ void interpreter::add_constructor(string const &name, shared_ptr chb, callback_base * cb, policies const &p) { +void interpreter::add_managed_constructor(string const &name, class_handler_base * chb, callback_base * cb) { constructor_handler_client_data_t * up = new constructor_handler_client_data_t; up->cb = cb; up->chb = chb; @@ -949,7 +1029,7 @@ void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST obj throw tcl_error("Factory was registered for unknown class."); } - class_handler_base *chb = oit->second.get(); + class_handler_base *chb = oit->second; // register a new command for the object returned // by this factory function @@ -957,7 +1037,24 @@ void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST obj // new object in the 'pXXX' form // - the new command will be created with this name - Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(chb), 0); + Tcl_Obj * to = Tcl_GetObjResult(interp); + void * p = nullptr; + if (to->internalRep.otherValuePtr) { + p = to->internalRep.otherValuePtr; + } else { + string const str(Tcl_GetString(to) + interpreter::object_namespace_prefix_len); + istringstream ss(str); + ss >> p; + } + + + auto * cd = new method_handler_client_data; + cd->obj = p; + cd->chb = chb; + cd->unroll = false; + cd->interp = nullptr; + + Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(cd), 0); } // process all declared sinks @@ -983,4 +1080,35 @@ void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST obj } } } + +tcl_error::tcl_error(std::string const & msg) : msg_(msg), std::runtime_error(msg) { + backtrace_size_ = backtrace(backtrace_, sizeof(backtrace_) / sizeof(backtrace_[0])); +} +tcl_error::tcl_error(Tcl_Interp * interp) : std::runtime_error(Tcl_GetString(Tcl_GetObjResult(interp))), msg_(Tcl_GetString(Tcl_GetObjResult(interp))) {} +const char * tcl_error::what() const throw() { + std::ostringstream oss; + oss << msg_ << "\n"; + char ** b = backtrace_symbols(backtrace_, backtrace_size_); + for (int i = 0; i < backtrace_size_; ++i) { + bool done = false; + const char * pos = index(b[i], '('); + if (pos) { + ++pos; + const char * epos = index(pos, '+'); + if (epos) { + oss << std::string(b[i], pos - b[i]); + std::string str(pos, epos - pos); + oss << boost::core::demangle(str.c_str()); + oss << epos << "\n"; + done = true; + } + } + if (! done) { + oss << boost::core::demangle(b[i]) << "\n"; + } + } + free(b); + return strdup(oss.str().c_str()); +} + } diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index 6ec1d89..b0f3cdb 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include // // Using TCL stubs is the default behavior @@ -56,9 +58,13 @@ namespace Tcl { // 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))) {} + std::string msg_; + void * backtrace_[16]; + size_t backtrace_size_; +public: + explicit tcl_error(std::string const &msg); //: std::runtime_error(msg) {} + explicit tcl_error(Tcl_Interp *interp); + const char * what() const throw(); }; class tcl_usage_message_printed { @@ -85,12 +91,14 @@ struct policies { bool variadic_; std::string usage_; std::string options_; + bool has_postprocess() { + return !factory_.empty() || ! sinks_.empty(); + } }; // syntax short-cuts policies factory(std::string const &name); policies sink(int index); -//policies variadic(); policies usage(std::string const &message); policies options(std::string const & options); @@ -99,29 +107,16 @@ class object; void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod); +struct named_pointer_result { + std::string name; + void * p; + template + named_pointer_result(std::string const & n, U * u) : name(n), p((void *) u) { } +}; + namespace details { template struct tcl_cast; -template struct tcl_cast { - static T *from(Tcl_Interp *, Tcl_Obj *obj, bool byReference) { - std::string s(Tcl_GetString(obj)); - if (s.size() == 0) { - throw tcl_error("Expected pointer value, got empty string."); - } - - if (s[0] != 'p') { - throw tcl_error("Expected pointer value."); - } - - std::istringstream ss(s); - char dummy; - void *p; - ss >> dummy >> p; - - return static_cast(p); - } -}; - // the following partial specialization is to strip reference // (it returns a temporary object of the underlying type, which // can be bound to the const-ref parameter of the actual function) @@ -169,6 +164,7 @@ 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); +void set_result(Tcl_Interp *interp, named_pointer_result); // helper for checking for required number of parameters // (throws tcl_error when not met) @@ -182,16 +178,18 @@ class callback_base { public: virtual ~callback_base() {} - virtual void invoke(void * dummyp, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; + virtual void invoke(void * p, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; + virtual void install() = 0; + virtual void uninstall() = 0; }; // base class for object command handlers // and for class handlers -class object_cmd_base { +class object_cmd_base : public callback_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[], bool object_dot_method) = 0; + //virtual ~object_cmd_base() {} + //virtual void invoke(void *p, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; }; // base class for all class handlers, still abstract @@ -201,14 +199,26 @@ class class_handler_base : public object_cmd_base { class_handler_base(); - void register_method(std::string const &name, std::shared_ptr ocb, Tcl_Interp * interp, bool managed); + void register_method(std::string const &name, callback_base * ocb, Tcl_Interp * interp, bool managed); //policies &get_policies(std::string const &name); void install_methods(Tcl_Interp * interp, const char * prefix, void * obj); void uninstall_methods(Tcl_Interp * interp, const char * prefix); + ~class_handler_base() { + for (auto & r : methods_) { + delete r.second; + delete[] r.first; + } + } protected: - typedef std::map > method_map_type; + struct str_less { + bool operator()(const char * a, const char * b) const { + return strcmp(a, b) < 0; + } + }; + + typedef std::map method_map_type; // a map of methods for the given class method_map_type methods_; @@ -217,31 +227,28 @@ class class_handler_base : public object_cmd_base { }; // class handler - responsible for executing class methods -template class class_handler : public class_handler_base { +template +class class_handler : public class_handler_base { public: virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) { - C *p = static_cast(pv); - - //if (objc < 2) { - // throw tcl_error(pol.usage_); - //} + C * p = static_cast(pv); - std::string methodName(Tcl_GetString(objv[1])); - if (objc == 2 && methodName == "-delete") { + char * methodName = Tcl_GetString(objv[1]); + if (objc == 2 && strcmp(methodName, "-delete") == 0) { 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."); + throw tcl_error("Method " + std::string(methodName) + " not found."); } it->second->invoke(pv, interp, objc, objv, false); } + void install() { } + void uninstall() { } }; } // details @@ -253,16 +260,28 @@ struct optional { T val; bool valid; operator bool() const { return valid; } - T const & operator*() const { return val; } - T const * operator->() const { return &val; } + T const & operator*() const { + if (!valid) { + throw tcl_error("retrieving value of unspecified optional attempted"); + } + return val; + } + T const * operator->() const { + if (!valid) { + throw tcl_error("retrieving value of unspecified optional attempted"); + } + return &val; + } optional() : valid(false) { } optional(T) : valid(false) { } optional(T t, bool v) : val(t), valid(v) { } + operator std::optional() { return valid ? std::optional(val) : std::optional(); } }; template struct getopt : public optional { using optional::optional; + using optional::operator std::optional; }; template @@ -297,6 +316,17 @@ struct is_basic_type { } // details +template +struct overloaded { + std::tuple ts; +}; + +template +overloaded overload(Ts... ts) { + return overloaded{ { ts... } }; +}; + + template struct list { interpreter * interp_; @@ -315,14 +345,12 @@ struct list { list(list const & o) : interp_(o.interp_), lo_(o.lo_), ot_(o.ot_), list_owner_(o.list_owner_) { if (list_owner_) { - //std::cerr << "copy list\n"; Tcl_IncrRefCount(lo_); } } ~list() { if (list_owner_) { - //std::cerr << "~list " << lo_->refCount << "\n"; Tcl_DecrRefCount(lo_); } } @@ -353,8 +381,12 @@ struct list { } std::size_t size() const ; - return_t at(std::size_t ix) const; + return_t at(std::size_t ix) const { + return with_obj_at(ix).first; + } return_t operator[](std::size_t ix) const { return at(ix); } + std::pair with_obj_at(std::size_t ix) const; + Tcl_Obj * obj_at(std::size_t ix) const; }; template @@ -390,11 +422,6 @@ struct variadic : public policies { } }; -template -struct overload { - -}; - namespace details { template @@ -459,7 +486,7 @@ struct all_lists { template struct all_lists > : public all_lists_impl { }; - + template struct any_impl { Tcl_Obj * o_; @@ -514,6 +541,8 @@ struct any : public details::any_impl { Tcl_DecrRefCount(this->o_); } } + + object as_object(); operator bool() const { for (int i = 0; i < sizeof...(Ts); ++i) { @@ -524,13 +553,19 @@ struct any : public details::any_impl { } template - void visit(Fs... f) const; + bool visit(Fs... f) const; template typename std::conditional::value, TT, TT *>::type as() const; }; -template struct init { }; +namespace details { struct init_default_tag { }; } + +template struct init { + bool default_; + init() : default_(false) { } + init(details::init_default_tag) : default_(true) { } +}; namespace details { @@ -624,7 +659,8 @@ struct getopt_index { template struct visit_impl { template - static void invoke(any const &) { + static bool invoke(any const &) { + return false; } }; @@ -635,32 +671,49 @@ struct argument_type { }; template struct argument_type { - typedef typename std::conditional::type, T>::type * - >::value, T, typename argument_type::type>::type type; + typedef typename std::conditional::type, T>::type arg1_t; + + typedef typename std::conditional< + std::is_invocable::value || std::is_invocable::value, + T, + typename argument_type::type + >::type type; }; template struct visit_impl { template - static void invoke(any const & a, F f, Fs... fs) { + static bool invoke(any const & a, F f, Fs... fs) { typedef typename argument_type::type arg_t; typedef typename argument_type::type arg_list_t; - if constexpr (!std::is_same::value && !std::is_same::value) { - if (arg_list_t li = a.template as()) { - for (auto && i : li) { - f(i); + if constexpr (! std::is_same::value || ! std::is_same::value) { + if constexpr (!std::is_same::value) { + if (arg_list_t li = a.template as()) { + if constexpr (std::is_invocable::value) { + for (auto && i : li) { + f(i); + } + } else if constexpr (std::is_invocable::value) { + for (std::size_t i = 0; i < li.size(); ++i) { + auto both = li.with_obj_at(i); + f(both.first, both.second); + } + } + return true; + } + } else { + if (arg_t * p = a.template as()) { + if constexpr (std::is_invocable::value) { + f(p); + } else if constexpr (std::is_invocable::value) { + f(p, nullptr); + } + return true; } - return; - } - } else if constexpr (!std::is_same::value) { - if (arg_t * p = a.template as()) { - f(p); - return; } } - visit_impl::invoke(a, fs...); + return visit_impl::invoke(a, fs...); } }; inline std::string tcl_typename(Tcl_Obj * o) { @@ -670,29 +723,36 @@ inline std::string tcl_typename(Tcl_Obj * o) { template template -void any::visit(Fs... f) const { - details::visit_impl::invoke(*this, f...); +bool any::visit(Fs... f) const { + return details::visit_impl::invoke(*this, f...); } namespace details { template struct generate_hasarg { - static void invoke(bool * arr, std::string *, const char *) { } + static void invoke(bool * arr, std::string *, std::string *, const char *, bool) { } }; template struct generate_hasarg { - static void invoke(bool * arr, std::string * opts, const char * p) { + static void invoke(bool * arr, std::string * opts, std::string * defaults, const char * p, bool may_have_defaults) { if (!is_getopt::value) { - generate_hasarg::invoke(arr, opts, p); + generate_hasarg::invoke(arr, opts, defaults, p, may_have_defaults); } else { arr[I] = !std::is_same::type, getopt >::value; while (*p && isspace(*p)) ++p; if (p) { const char * pend = p; - while (*pend && !isspace(*pend)) ++pend; + while (*pend && !isspace(*pend) && *pend != '=') ++pend; opts[I] = std::string(p, pend - p); - - generate_hasarg::invoke(arr, opts, pend); + if (*pend == '=') { + if (!may_have_defaults) { + throw tcl_error("function is not allowed to have defaults"); + } + ++pend; p = pend; + while (*pend && !isspace(*pend)) ++pend; + defaults[I] = std::string(p, pend - p); + } + generate_hasarg::invoke(arr, opts, defaults, pend, may_have_defaults); } else { std::cerr << "option error\n"; } @@ -700,7 +760,6 @@ struct generate_hasarg { } }; - template struct no_type { }; @@ -792,7 +851,6 @@ struct subtype<0, variadic, false> { typedef typename subtype<0, T>::type type; }; - template struct num_subtypes { static const int value = 1 + 100000 - subtype_notype_tag::type>::value; @@ -804,7 +862,6 @@ struct generate_argtypes { static const int length = 0; }; - template struct fix_variadic_return { typedef typename std::decay::type type; @@ -844,33 +901,148 @@ TT do_cast(ppack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj struct no_init_type {}; struct no_class { }; +struct no_class_lambda { }; + template struct class_lambda { }; -template -struct class_lambda_unpack { - typedef C type; +template +struct conversions { }; -template -struct class_lambda_unpack > { - typedef C type; + +template +struct conversion_ix { }; +template +struct conversion_t { }; + +template +struct make_conversions { }; -template -struct is_class_lambda { - static const bool value = !std::is_same::type>::value; +struct default_conversion { +}; + +struct conversions_end { +}; + +struct conversion_error { +}; + +template +struct make_conversions, conversion_t > { + typedef conversions_end type; + typedef conversions_end recurse; +}; + +template +struct make_conversions, conversion_t > { + typedef typename std::conditional::type type; + typedef typename std::conditional + , conversion_t >, + make_conversions, conversion_t > >::type recurse; +}; + +template +struct conversion_at { + typedef typename std::conditional + ::type>::type type; +}; + +template +struct conversion_at { + typedef conversion_error type; +}; + +struct callback_empty_base { }; +template +struct callback_no_baseclass { }; // policy tag + +template +struct callback_base_type { + typedef object_cmd_base type; +}; +template <> +struct callback_base_type { + typedef callback_base type; +}; +template +struct callback_free_method { }; // policy tag + +template +struct callback_postproc { }; // policy tag + +template +struct callback_attributes { }; + +template +struct callback_base_type > { + typedef callback_empty_base type; +}; + +template +struct attributes { + static const bool cold = cold_; +}; +template +struct is_attribute { + static const bool value = false; +}; + +template +struct is_attribute > { + static const bool value = true; +}; + +template +struct callback_policy_unpack { + typedef T type; + static const bool no_base = false; + static const bool free_method = false; + static const bool class_lambda = false; + static const bool postproc = true; + typedef attributes<> attr; }; -template class callback_v : public std::conditional::value, details::callback_base, details::object_cmd_base>::type { - typedef typename class_lambda_unpack::type C; +template +struct callback_policy_unpack > : public callback_policy_unpack { + static const bool no_base = P; +}; +template +struct callback_policy_unpack > : public callback_policy_unpack { + static const bool postproc = P; +}; + +template +struct callback_policy_unpack > : public callback_policy_unpack { + typedef A attr; +}; + +template +struct callback_policy_unpack > : public callback_policy_unpack { + static const bool free_method = true; +}; +template +struct callback_policy_unpack > : public callback_policy_unpack { + static const bool class_lambda = true; +}; + +template +class callback_v : public callback_base_type::type { + typedef callback_v this_t; + + typedef callback_policy_unpack pol_t; + typedef typename pol_t::type C; + + static const bool may_have_defaults = false; typedef Fn functor_type; policies policies_; functor_type f_; static const int num_opt = num_getopt::value; - std::string opts_[num_opt]; + std::string opts_[num_opt], defaults_[num_opt]; bool has_arg_[num_opt + 1] = { false }; @@ -879,65 +1051,220 @@ template class callbac Tcl_ObjType * argument_types_all_[generate_argtypes<0, Ts...>::length + 1]; interpreter * interpreter_; - public : - callback_v(interpreter * i, functor_type f, std::string const & name, policies const & pol) : interpreter_(i), policies_(pol), f_(f) { + Conv conv_; + + std::string name_; + + static const bool cold = pol_t::attr::cold; //has_attribute::value;//false && pol_t::class_lambda; +public : + static const int num_arguments = (std::is_same::value || pol_t::free_method ? 0 : 1) + sizeof...(Ts); + +#ifdef COLD +#error fixme +#else +#define COLD __attribute__((optimize("s"))) __attribute__((cold)) +#endif + + callback_v(interpreter * i, functor_type f, std::string const & name, policies const & pol = policies(), Conv conv = Conv()) COLD : interpreter_(i), policies_(pol), f_(f), conv_(conv), name_(name) { if (num_opt) { if (pol.options_.empty()) { throw tcl_error(std::string("no getopt string supplied for function \"") + name + "\" taking getopt<> arguments"); } - generate_hasarg<0, Ts...>::invoke(has_arg_, opts_, pol.options_.c_str()); + generate_hasarg<0, Ts...>::invoke(has_arg_, opts_, defaults_, pol.options_.c_str(), may_have_defaults); } - //std::cerr << "genargtype\n"; generate_argtypes<0, Ts...>::invoke(i, argument_types_all_); } + void install() __attribute__((optimize("s"))); + void uninstall() __attribute__((optimize("s"))); + void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { + checked_invoke_impl(pv, interp, argc, argv, object_dot_method); + } + void invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) COLD; + + ~callback_v() COLD { } +private : template struct void_return { }; template std::pair args() { return generate_argtypes<0, Ts...>::template get(argument_types_all_); } - - // regular function (return / void) - template ::value, bool>::type = true> - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) { - if (argc && argv) { } - details::set_result(interp, f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); - } - template ::value, bool>::type = true> - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { - if (argc && argv && interp) { } - f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); - } + template + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) __attribute__((optimize(cold ? "s" : "3"))); + template + void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) __attribute__((optimize(cold ? "s" : "3"))); + + int checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + + static int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) __attribute__((optimize(cold ? "s" : "3"))); - // method implemented by a lambda (return / void) - template ::value, bool>::type = true> - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { - if (argc && argv) { } - details::set_result(interp, f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); +}; + +template +template +void callback_v::do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) { + if (argc && argv) { } + if constexpr (pol_t::class_lambda) { + details::set_result(interp, f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } else if constexpr (std::is_same::value || pol_t::free_method) { + details::set_result(interp, f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } else { + details::set_result(interp, (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); } - template ::value, bool>::type = true> - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { - if (argc && argv && interp) { } - f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); +} + +template +template +void callback_v::do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { + if (argc && argv && interp) { } + if constexpr (pol_t::class_lambda) { + f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } else if constexpr (std::is_same::value || pol_t::free_method) { + f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } else { + (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); } +} + + +template , typename E = void> +struct callback_expand_f { +}; + +template +struct callback_expand_f::value>::type> { + typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; + +template +struct callback_expand_f::value>::type> { + typedef callback_v, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; + +template +struct callback_expand_f, postproc, no_base, Attr> { + typedef callback_v, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; + +template +struct senti { }; +template +struct callback_expand_f2 { + typedef typename senti::type type; +}; + +template +struct callback_expand_f2 { + typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; + +template +struct is_std_function { + static const bool value = false; +}; +template +struct is_std_function > { + static const bool value = true; +}; - // method (return / void) - template ::value && !std::is_same::value, bool>::type = true> - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { - if (argc && argv) { } - details::set_result(interp, (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...)); +template +struct callback_expand_f::value && !is_std_function::value>::type> { + typedef typename callback_expand_f2::type type; +}; + +template +struct num_arguments { + static const int value = -1; +}; + +template +struct num_arguments { + static const int value = sizeof...(Ts); +}; + +template +static void overload_enumerate(Fn fn, Tup const & tup) { + fn(&std::get(tup)); + if constexpr (Ix < std::tuple_size::value - 1) { + overload_enumerate(fn, tup); } - template ::value && !std::is_same::value, bool>::type = true> - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { - if (argc && argv && interp) { } - (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol)...); +} + +template +struct overloaded_callback : public callback_base_type::type { + typedef overloaded_callback this_t; + typedef callback_policy_unpack pol_t; + typedef typename pol_t::type Cparm_out; + + static const bool cold = pol_t::attr::cold; + + interpreter * interpreter_; + std::string name_; + + std::tuple>::type...> callbacks_; + + overloaded_callback(interpreter * i, overloaded f, std::string const & name, policies const & pol, Conv conv) + : overloaded_callback(std::make_index_sequence(), i, f, name, pol, conv) { } + + template + overloaded_callback(std::index_sequence const &, interpreter * i, overloaded f, std::string const & name, policies const & pol, Conv conv) + : interpreter_(i), name_(name), callbacks_(typename callback_expand_f>::type(i, std::get(f.ts), name, pol, conv)...) { } + + template + void do_invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); - void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method); + static int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) __attribute__((optimize(cold ? "s" : "3"))); + int checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + void invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { + checked_invoke_impl(pv, interp, argc, argv, object_dot_method); + } + void install() COLD; + void uninstall() COLD; }; +template +template +void overloaded_callback::do_invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { + if (std::tuple_element::type::num_arguments + 1 == argc) { + std::get(callbacks_).invoke_impl(pv, interp, argc, argv, object_dot_method); + return; + } + if constexpr (ti + 1 < std::tuple_size::value) { + do_invoke(pv, interp, argc, argv, object_dot_method); + } else { + std::cerr << "wrong number of arguments, overload accepts any of:"; + overload_enumerate<0>([&] (auto * o) { + std::cerr << " " << std::remove_pointer::type::num_arguments; + }, callbacks_); + std::cerr << " arguments\n"; + + } +} + template struct std_function { typedef void type; @@ -946,6 +1273,87 @@ template struct std_function { typedef std::function type; }; +} + +template +struct lambda_wrapper { Fn fn; lambda_wrapper(Fn f) : fn(f) { } }; + +namespace details { +template +struct is_lambda_wrapper { + static const bool value = false; +}; +template +struct is_lambda_wrapper > { + static const bool value = true; +}; + +inline policies default_policies; + +template +inline policies const & extra_args_policies(E const & e, Extra... extra) { + if constexpr (std::is_same::type, policies>::value) { + return e; + } + return default_policies; +} + +inline policies const & extra_args_policies() { + return default_policies; +} + +inline auto extra_args_conversions() { + return std::tuple<>(); +} + +template +struct has_policies { + static const bool value = false; +}; + +template +struct has_policies { + static const bool value = std::is_same::value ? true : has_policies::value; +}; + +template +inline auto extra_args_conversions(E e, Extra... extra) { + if constexpr (std::is_same::type, policies>::value) { + return extra_args_conversions(extra...); + } else if constexpr (is_lambda_wrapper::value) { + return extra_args_conversions(extra...); + } else if constexpr (is_attribute::value) { + return extra_args_conversions(extra...); + } else { + return std::tuple(e, extra...); + } +} + +template +inline auto apply_extra_args_wrappers(Fn fn) { + return fn; +} + +template +inline auto apply_extra_args_wrappers(Fn fn, E e, Extra... extra) { + if constexpr (std::is_same::type, policies>::value) { + return apply_extra_args_wrappers(fn, extra...); + } else if constexpr (is_lambda_wrapper::value) { + return e.fn(fn); + } else { + return fn; + } +} +template +struct attributes_merge { + typedef attributes<> type; +}; + +template +struct attributes_merge { + typedef typename std::conditional::value, E, typename attributes_merge::type>::type type; +}; + } // namespace details extern details::no_init_type no_init; @@ -1166,97 +1574,141 @@ class interpreter { } int trace_count_ = 0; + + template , typename R=void, typename ...Ts> + using callback_t = typename details::callback_expand_f::type; + + template + static inline std::integral_constant arg; + + struct { + } res; + + static constexpr details::attributes cold = { }; + + template + static auto eac(Extra... e) { + return details::extra_args_conversions(e...); + } + template + static auto eap(Extra... e) { + return details::extra_args_policies(e...); + } + template + static auto eaw(Fn fn, Extra ... e) { + return details::apply_extra_args_wrappers(fn, e...); + } - template class class_definer { + template + class class_definer { + std::tuple with_extra; public: - class_definer(std::shared_ptr> ch, interpreter * inter, bool managed) : ch_(ch), interp_(inter), managed_(managed) {} + class_definer(details::class_handler * ch, interpreter * inter, bool managed, WExtra... extra) : ch_(ch), interp_(inter), managed_(managed), with_extra(extra...) {} - template - class_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { - return def2(name, fn, p); + template + class_definer & def(std::string const & name, Fn fn, Extra... extra) { + //return def2(name, fn, std::make_index_sequence(), extra...); + return def2(name, fn, std::make_index_sequence(), extra...); } - template - class_definer & def2(std::string const & name, R (C::*f)(Ts...), policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, name, p)), interp_->get_interp(), managed_); - return *this; - } - template - class_definer & def2(std::string const & name, R (C::*f)(Ts...) const, policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::callback_v(interp_, f, name, p)), interp_->get_interp(), managed_); - return *this; - } - template - class_definer & def2(std::string const & name, std::function fn, policies const & p = policies()) { - ch_->register_method(name, std::shared_ptr(new details::callback_v, std::function, R, Ts...>(interp_, fn, name, p)), interp_->get_interp(), managed_); - return *this; - } + template + class_definer & def2(std::string const & name, Fn f, std::index_sequence, Extra... extra) COLD; - template - class_definer & def2(std::string const & name, Fn fn, policies const & p = policies()) { - return def2(name, typename details::std_function::type(fn), p); - } + template + class_definer & def2(std::string const & name, overloaded const & over, std::index_sequence seq, Extra... extra) COLD; - template - class_definer & defvar(std::string const & name, T C::*varp); + template + class_definer & defvar(std::string const & name, T CC::*varp) COLD; private: interpreter * interp_; - std::shared_ptr> ch_; + details::class_handler * ch_; bool managed_; }; - template + template struct function_definer { - function_definer(C * this_p, interpreter * interp) : this_p_(this_p), interp_(interp) { } + std::tuple with_extra; + + function_definer(C * this_p, interpreter * interp, WExtra... extra) : this_p_(this_p), interp_(interp), with_extra(extra...) { } - template - function_definer & def(std::string const & name, Fn fn, policies const & p = policies()) { - interp_->def(name, fn, this_p_, p); - return *this; + template + function_definer & def(std::string const & name, Fn fn, Extra... extra) { + return def2(name, fn, std::make_index_sequence(), extra...); } + template + function_definer & def2(std::string const & name, Fn fn, std::index_sequence, Extra... extra) COLD; + template - function_definer & defvar(std::string const & name, T C::*t); + function_definer & defvar(std::string const & name, T C::*t) COLD; C * this_p_; interpreter * interp_; }; + + const Tcl_ObjType * list_type_ = nullptr; + const Tcl_ObjType * cmdname_type_ = nullptr; + const Tcl_Namespace * object_namespace_ = nullptr; + //const Tcl_Namespace * object_commandnamespace_ = nullptr; + + bool is_list(Tcl_Obj * o) { return o->typePtr == list_type_; } + + static constexpr const char * object_namespace_name = "o"; + static constexpr const char * object_namespace_prefix = "o::"; + static const int object_namespace_prefix_len = 3; + +#if 0 + static constexpr const char * object_command_namespace_name = "O"; + static constexpr const char * object_command_namespace_prefix = "O::"; + static const int object_command_namespace_prefix_len = 3; +#endif + void find_standard_types(); interpreter(Tcl_Interp *, bool owner = false); ~interpreter(); void make_safe(); - Tcl_Interp *get() const { return interp_; } + void custom_construct(const char * classname, const char * objname, void * p); + + Tcl_Interp * get() const { return interp_; } Tcl_Interp * get_interp() { return interp_; } - + // free function definitions - template - void def(std::string const & name, R(*f)(Ts...), policies const & p = policies()) { - add_function(name, new details::callback_v(this, f, name, p)); + template + void def(std::string const & name, Fn fn, Extra... extra) { + auto pol = eap(extra...); + + def2::value>(name, fn, extra...); } - template - void def(std::string const & name, std::function fn, policies const & p = policies()) { - add_function(name, new details::callback_v, R, Ts...>(this, fn, name, p)); + template + void def2(std::string const & name, Fn fn, Extra... extra) { + typedef decltype(details::extra_args_conversions(extra...)) conv_t; + add_function(name, new callback_t(this, fn, name, eap(extra...), eac(extra...))); } - template - void def(std::string const & name, Fn fn, policies const & p = policies()) { - def(name, typename details::std_function::type(fn), p); + template + void def(std::string const & name, R (C::*f)(Ts...), C * this_p, Extra... extra) { + def(name, [this_p, f](Ts... args) { return (this_p->*f)(args...); }, extra...); } - - template - void def(std::string const & name, R (C::*f)(Ts...), C * this_p, policies const & p = policies()) { - def(name, [this_p, f](Ts... args) { return (this_p->*f)(args...); }, p); + + template + void def(std::string const & name, overloaded over, Extra... extra) { + typedef decltype(details::extra_args_conversions(extra...)) conv_t; + add_function(name, new details::overloaded_callback(this, over, name, eap(extra...), eac(extra...))); } - + template void defvar(std::string const & name, T & v); - template - function_definer def(C * this_p) { - return function_definer(this_p, this); + template + function_definer with(C * this_p, Extra... extra) { + return function_definer(this_p, this, extra...); + } + template + function_definer with(Extra... extra) { + return function_definer(nullptr, this, extra...); } template struct call_constructor { @@ -1265,20 +1717,12 @@ class interpreter { } }; - template class_definer class_(std::string const &name, init const & = init(), policies const &p = policies()) { - typedef details::callback_v callback_type; - - std::shared_ptr> ch(new details::class_handler()); - - add_class(name, ch); - - add_constructor(name, ch, new callback_type(this, &call_constructor::doit, name, p), p); + template + class_definer class_(std::string const &name, init const & init_arg = init(details::init_default_tag()), Extra... extra) COLD; - return class_definer(ch, this, false); - } + template + class_definer class_(std::string const &name, details::no_init_type const &, Extra... extra) COLD; -#define CPPTCL_MANAGED_CLASS -#ifdef CPPTCL_MANAGED_CLASS template struct call_managed_constructor { interpreter * interp_; call_managed_constructor(interpreter * i) : interp_(i) { } @@ -1286,20 +1730,34 @@ class interpreter { object operator()(Ts... ts); }; - template class_definer managed_class_(std::string const &name, init const & = init(), policies const &p = policies()) { - typedef details::callback_v, object, Ts...> callback_type; - - std::shared_ptr> ch(new details::class_handler()); + template + class_definer managed_class_(std::string const &name, init const & = init(), Extra... extra) { + typedef details::callback_v, call_managed_constructor, object, Ts...> callback_type; + + if (get_class_handler(name)) { + throw tcl_error("overloaded constructors not implemented"); + } + + details::class_handler * ch = new details::class_handler(); + + this->type >(name, (C *) 0); - this->type >(name, (C *) 0); - add_class(name, ch); - add_managed_constructor(name, ch, new callback_type(this, call_managed_constructor(this), name, p), p); + add_managed_constructor(name, ch, new callback_type(this, call_managed_constructor(this), name)); - return class_definer(ch, this, true); + return class_definer(ch, this, true, extra...); + } + + template + class_definer managed_class_(std::string const &name, details::no_init_type const &, Extra... extra) { + details::class_handler * ch = static_cast *>(get_class_handler(name)); + if (!ch) { + ch = new details::class_handler(); + add_class(name, ch); + } + return class_definer(ch, this, true, extra...); } -#endif std::map obj_type_by_tclname_; std::map obj_type_by_cppname_; @@ -1312,7 +1770,7 @@ class interpreter { } virtual deleter * dup_deleter() { return this; } virtual void * dup(void * obj) { - return (void *) new T(*((T *) obj)); + return obj; } virtual ~deleter() { } }; @@ -1320,7 +1778,6 @@ class interpreter { template struct dyn_deleter : public deleter { bool free(Tcl_Obj * o) override { - //std::cerr << "REF#: " << o->refCount << "\n"; delete (T *) o->internalRep.twoPtrValue.ptr1; return true; } @@ -1335,31 +1792,24 @@ class interpreter { template shared_ptr_deleter(T * p, DEL del) : p_(p, del) { - //std::cerr << "custom\n"; } shared_ptr_deleter(std::shared_ptr & p) : p_(p) { - //std::cerr << "copy\n"; } bool free(Tcl_Obj * o) override { return true; } virtual void * dup(void * obj) override { return obj; } deleter * dup_deleter() override { - //std::cerr << "dup\n"; return new shared_ptr_deleter(p_); } ~shared_ptr_deleter() { - //std::cerr << "~shptr deleter\n"; } }; - template + template struct type_ops { static void dup(Tcl_Obj * src, Tcl_Obj * dst) { - //2p dst->internalRep.otherValuePtr = src->internalRep.otherValuePtr; - //std::cerr << "dup " << src->typePtr << " " << (src->typePtr ? src->typePtr->name : "()" ) << "\n"; if (src->internalRep.twoPtrValue.ptr2) { - //std::cerr << "dup osrc=" << src << "[" << src->refCount << "] odst=" << dst << "[" << dst->refCount << "]\n";// type=" << src->typePtr << " intern=" << src->internalRep.otherValuePtr << "\n"; deleter * d = (deleter *) src->internalRep.twoPtrValue.ptr2; dst->internalRep.twoPtrValue.ptr1 = d->dup(src->internalRep.twoPtrValue.ptr1); dst->internalRep.twoPtrValue.ptr2 = (( deleter *)src->internalRep.twoPtrValue.ptr2)->dup_deleter(); @@ -1367,12 +1817,10 @@ class interpreter { dst->internalRep.twoPtrValue.ptr1 = src->internalRep.twoPtrValue.ptr1; dst->internalRep.twoPtrValue.ptr2 = nullptr; } - //src->internalRep.twoPtrValue.ptr2 = nullptr; dst->typePtr = src->typePtr; } static void free(Tcl_Obj * o) { if (o->internalRep.twoPtrValue.ptr2) { - //std::cerr << "free " << o << "\n"; deleter * d = (deleter *) o->internalRep.twoPtrValue.ptr2; if (d->free(o)) { delete d; @@ -1381,7 +1829,7 @@ class interpreter { } static void str(Tcl_Obj * o) { std::ostringstream oss; - oss << 'p' << o->internalRep.twoPtrValue.ptr1; + oss << (Managed ? object_namespace_name : object_namespace_name) << "::" << o->internalRep.twoPtrValue.ptr1; str_impl(o, oss.str()); } static void str_impl(Tcl_Obj * o, std::string const & name) { @@ -1391,14 +1839,12 @@ class interpreter { o->length = name.size(); } static int set(Tcl_Interp *, Tcl_Obj *) { - //std::cerr << "type set\n"; return TCL_OK; } }; template Tcl_ObjType * get_objtype() { - //std::cerr << "get_objtype\n"; auto it = obj_type_by_cppname_.find(std::type_index(typeid(TY))); if (it != obj_type_by_cppname_.end()) { return it->second; @@ -1406,28 +1852,17 @@ class interpreter { return nullptr; } Tcl_ObjType * get_objtype(const char * name) { - //std::cerr << "get_objtype\n"; auto & o = obj_type_by_tclname_; auto it = o.find(name); return it == o.end() ? nullptr : it->second; } template bool is_type(Tcl_Obj * o) { - //std::cerr << "is_type\n"; return o->typePtr && get_objtype() == o->typePtr; - if (o->typePtr) { - std::type_index * tip = (std::type_index *) (o->typePtr->name + 256); - if (std::type_index(typeid(T)) == *tip) { - return true; - } - } - return false; } template T * try_type(Tcl_Obj * o) { - //std::cerr << "try_type\n"; if (is_type(o)) { - //2p return (T *) o->internalRep.otherValuePtr; return (T *) o->internalRep.twoPtrValue.ptr1; } return nullptr; @@ -1445,7 +1880,7 @@ class interpreter { std::cerr << "attempt to register type " << name << " twice\n"; } Tcl_ObjType * ot = new Tcl_ObjType; - //char * cp = new char[name.size() + 1 + sizeof(std::type_index)]; + char * cp = new char[256 + sizeof(std::type_index)]; strcpy(cp, name.c_str()); cp[name.size()] = 0; @@ -1479,11 +1914,20 @@ class interpreter { return this->template tcl_cast(this->get_interp(), o, false, ot); } - template ::value, bool>::type = true> + template ::value && std::is_same::type>::type, char>::value, bool>::type = true> + T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { + return Tcl_GetStringFromObj(o, nullptr); + } + + template ::value>::type = true> + T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { + return o; + } + + template ::value && !std::is_same::type>::type, char>::value, bool>::type = true> T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { auto it = obj_type_by_cppname_.find(std::type_index(typeid(typename std::remove_pointer::type))); if (it != obj_type_by_cppname_.end()) { - //std::cerr << "castingold...\n"; return this->template tcl_cast(i, o, byref, it->second); } throw tcl_error("function argument type is not registered"); @@ -1492,48 +1936,32 @@ class interpreter { template ::value, bool>::type = true> T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref, Tcl_ObjType * ot) { if (std::is_pointer::value && o->typePtr) { - //auto it = obj_type_by_cppname_.find(std::type_index(typeid(typename std::remove_pointer::type))); - //if (it != obj_type_by_cppname_.end()) { - if (true) { - // Tcl_ObjType * otp = it->second; - Tcl_ObjType * otp = ot; - //std::cerr << "casting... " << otp << " " << o->typePtr << " " << o->typePtr->name << "\n"; - - if (otp == o->typePtr) { - //return (typename std::conditional::value, T, T*>::type) o->internalRep.otherValuePtr; - //std::cerr << "objcast " << o << " " << o->internalRep.otherValuePtr << "\n"; - //return (T) o->internalRep.otherValuePtr; - return (T) o->internalRep.twoPtrValue.ptr1;//otherValuePtr; - } else if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { - int len; - if (Tcl_ListObjLength(i, o, &len) == TCL_OK) { - if (len == 1) { - Tcl_Obj *o2; - if (Tcl_ListObjIndex(i, o, 0, &o2) == TCL_OK) { - if (otp == o2->typePtr) { - //std::cerr << "objcast(list=len1) " << o2 << " " << o2->internalRep.otherValuePtr << "\n"; - //2p return (T) o2->internalRep.otherValuePtr; - return (T) o2->internalRep.twoPtrValue.ptr1; - } + Tcl_ObjType * otp = ot; + if (otp == o->typePtr) { + return (T) o->internalRep.twoPtrValue.ptr1;//otherValuePtr; + } else if (is_list(o)) { + int len; + if (Tcl_ListObjLength(i, o, &len) == TCL_OK) { + if (len == 1) { + Tcl_Obj *o2; + if (Tcl_ListObjIndex(i, o, 0, &o2) == TCL_OK) { + if (otp == o2->typePtr) { + return (T) o2->internalRep.twoPtrValue.ptr1; } - } else { - throw tcl_error("Expected single object, got list"); } } else { - throw tcl_error("Unknown tcl error"); + throw tcl_error("Expected single object, got list"); } } else { - throw tcl_error("function argument has wrong type"); + throw tcl_error("Unknown tcl error"); } } else { - throw tcl_error("function argument type is not registered"); + throw tcl_error("function argument has wrong type"); } } else { throw tcl_error("function argument is not a tcl object type"); } return nullptr; - return details::tcl_cast::from(i, o, byref); - //return nullptr; } template ::value && std::is_same::type>::value && !details::is_any::value, bool>::type = true> T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { @@ -1544,23 +1972,6 @@ class interpreter { return details::tcl_cast::from(i, o, byref); } - template 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 class_definer(ch, this, false); - } - - template class_definer managed_class_(std::string const &name, details::no_init_type const &) { - std::shared_ptr> ch(new details::class_handler()); - - add_class(name, ch); - - return class_definer(ch, this, true); - } - - // free script evaluation details::result eval(std::string const &script); details::result eval(std::istream &s); @@ -1570,6 +1981,8 @@ class interpreter { void result_reset() { Tcl_ResetResult(get_interp()); } + + static std::string objinfo(object const & o); // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result eval(InputIterator first, InputIterator last); @@ -1617,10 +2030,11 @@ class interpreter { void add_function(std::string const &name, details::callback_base * cb); - void add_class(std::string const &name, std::shared_ptr chb); - - void add_constructor(std::string const &name, std::shared_ptr chb, details::callback_base * cb, policies const &p = policies()); - void add_managed_constructor(std::string const &name, std::shared_ptr chb, details::callback_base * cb, policies const &p = policies()); + void add_class(std::string const &name, details::class_handler_base * chb); + details::class_handler_base * get_class_handler(std::string const & name); + + void add_constructor(std::string const &name, details::class_handler_base * chb, details::callback_base * cb); + void add_managed_constructor(std::string const &name, details::class_handler_base * chb, details::callback_base * cb); Tcl_Interp *interp_; bool owner_; @@ -1641,61 +2055,42 @@ template T variadic::at(int ix) const { return interp_->template tcl_cast(objv[ix]); } template -typename list::return_t list::at(std::size_t ix) const { +Tcl_Obj * list::obj_at(std::size_t ix) const { + Tcl_Obj *o; + if (Tcl_ListObjIndex(interp_->get(), lo_, ix, &o) == TCL_OK) { + return o; + } +} + +template +std::pair::return_t, Tcl_Obj *> list::with_obj_at(std::size_t ix) const { + typedef std::pair ret_t; Tcl_Obj *o; if (Tcl_ListObjIndex(interp_->get(), lo_, ix, &o) == TCL_OK) { if constexpr (isany) { - return return_t(interp_, o, ot_); + return ret_t(return_t(interp_, o, ot_), o); } else if constexpr (details::is_basic_type::value) { - return interp_->template tcl_cast(o); + return ret_t(interp_->template tcl_cast(o), o); } else { if (o->typePtr == ot_[0]) { - //return (return_t) o->internalRep.otherValuePtr; - return (return_t) o->internalRep.twoPtrValue.ptr1; + return ret_t((return_t) o->internalRep.twoPtrValue.ptr1, o); } else { throw tcl_error("Unexpected type in list. Got " + details::tcl_typename(o) + " need " + ot_[0]->name); } } } - return return_t(); + return ret_t(return_t(), nullptr); } template template typename std::conditional::value, TT, TT *>::type any::as() const { - //if (this->interp_->try_type(this->o_->typePtr)) { -#if 0 - if (this->which_ == details::type_at::value) { - if constexpr (details::is_list::value) { - if (this->o_->typePtr && strcmp(this->o_->typePtr->name, "list") == 0) { - return TT(interp_, this->o_, interp_->get_objtype::type>()); - } else { - return TT(); - //throw tcl_error(std::string("bad cast of ") + (this->o_->typePtr ? this->o_->typePtr->name : "(none)") + " to list"); - } - } else { - //return (TT *) this->o_->internalRep.otherValuePtr; //o_->internalRep.otherValuePtr; - return (TT *) this->o_->internalRep.twoPtrValue.ptr1; //o_->internalRep.otherValuePtr; - } - } - if constexpr (details::is_list::value) { - return TT(); - } else { - return nullptr; - } -#else const int toff = sizeof...(Ts) - 1 - details::type_at::value; if constexpr (details::is_list::value) { - if (this->o_ && this->o_->typePtr && strcmp(this->o_->typePtr->name, "list") == 0) { + if (this->o_ && interp_->is_list(this->o_)) { Tcl_Obj * oo; if (Tcl_ListObjIndex(interp_->get_interp(), this->o_, 0, &oo) == TCL_OK) { -#if 0 - std::cerr << sizeof...(Ts) << " " << details::type_at::value << " " << details::tcl_typename(oo) << "\n"; - for (int i = 0; i < sizeof...(Ts); ++i) { - std::cerr << i << " " << this->ot_[i]->name << "\n"; - } -#endif if (oo->typePtr == this->ot_[toff]) { return TT(interp_, this->o_, this->ot_ + toff); } @@ -1703,20 +2098,36 @@ typename std::conditional::value, TT, TT *>::type anyo_->typePtr ? this->o_->typePtr->name : "(none)") + " to list"); } } else { - //std::cerr << this->o_->typePtr << " " << this->o_->typePtr->name << " " << details::type_at::value << " " << this->ot_ << "\n"; if (this->o_ && this->ot_[toff] == this->o_->typePtr) { return (TT *) this->o_->internalRep.twoPtrValue.ptr1; } else { return nullptr; } } -#endif } namespace details { +template struct tcl_cast { + static T *from(Tcl_Interp *, Tcl_Obj *obj, bool byReference) { + std::string s(Tcl_GetString(obj) + interpreter::object_namespace_prefix_len); + if (s.size() == 0) { + throw tcl_error("Expected pointer value, got empty string."); + } + + if (s.compare(0, interpreter::object_namespace_prefix_len, interpreter::object_namespace_prefix) != 0) { + throw tcl_error("Expected pointer value."); + } + + std::istringstream ss(s); + void *p; + ss >> p; + + return static_cast(p); + } +}; + template struct generate_argtypes { template @@ -1727,19 +2138,14 @@ struct generate_argtypes { template static Tcl_ObjType * lookup_type(interpreter * interp, int pos, wrap * np) { auto * ot = interp->get_objtype::type>(); - //std::cerr << "lookup " << pos << " " << "_Z" << typeid(TT).name() << " " << ot << " " << (ot ? ot->name : "()") << "\n"; return ot; - //return interp->get_objtype(); } template static void invoke_helper(std::index_sequence const &, interpreter * interp, Tcl_ObjType ** all) { ((all[Is] = lookup_type(interp, Is, (wrap::type> *) 0)), ...); - //p[0] = all; - //p[1] = all + sizeof...(is); generate_argtypes::invoke(interp, all + sizeof...(Is)); } static void invoke(interpreter * interp, Tcl_ObjType ** all) { - //std::cerr << "gat _Z" << typeid(T).name() << " " << num_subtypes::value << "\n"; invoke_helper(std::make_index_sequence::value>(), interp, all); } @@ -1755,11 +2161,11 @@ struct generate_argtypes { static const int length = num_subtypes::value + generate_argtypes::length; }; - template inline void any_impl::set_which(interpreter * i, Tcl_Obj * o) { if constexpr (is_list::value) { - if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { + //if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { + if (i->is_list(o)) { Tcl_Obj *oo; if (Tcl_ListObjIndex(i->get(), o, 0, &oo) == TCL_OK) { if (i->is_type::type>(oo)) { @@ -1793,19 +2199,54 @@ struct back { using type = typename std::conditional::type>::type; }; -template -void callback_v::invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { +template +void callback_v::install() { + Tcl_CreateObjCommand(interpreter_->get_interp(), name_.c_str(), callback_handler, (ClientData) this, 0); +} +template +void callback_v::uninstall() { + Tcl_DeleteCommand(interpreter_->get_interp(), name_.c_str()); +} + +template +int callback_v::checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { + try { + this->invoke_impl(pv, interp, argc, argv, object_dot_method); + } catch (tcl_usage_message_printed & e) { + } catch (std::exception & e) { + for (int i = 0; i < argc; ++i) { + if (i) { std::cerr << " "; } + if (this->interpreter_->is_list(argv[i])) { + std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; + } else { + std::cerr << Tcl_GetString(argv[i]); + } + } + std::cerr << ": " << e.what() << "\n"; + //Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); + return TCL_ERROR; + } catch (...) { + std::cerr << "Unknown error.\n"; + return TCL_ERROR; + } + return TCL_OK; +} + +template +void callback_v::invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { static const int num_gopt = num_getopt::value; - static const int num_opt = num_optional::value; + static const int num_opt = num_optional::value; Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; + bool getopt_allocated[num_gopt + 1] = { false }; Tcl_Obj boolean_dummy_object; - std::size_t ai = std::is_same::value || object_dot_method ? 1 : 2; + std::size_t ai = std::is_same::value || pol_t::free_method || object_dot_method ? 1 : 2; static const bool has_variadic_ = has_variadic::value; + bool any_getopt_allocated = false; if (interpreter_->trace()) { for (int i = 0; i < argc; ++i) { std::cerr << " "; - if (argv[i]->typePtr && strcmp(argv[i]->typePtr->name, "list") == 0) { + if (interpreter_->is_list(argv[i])) { std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; } else { std::cerr << Tcl_GetString(argv[i]); @@ -1815,7 +2256,7 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int } ++interpreter_->trace_count_; - if (argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0 && std::find(opts_, opts_ + num_gopt, "help") == opts_ + num_gopt) { + if (num_gopt && argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0 && std::find(opts_, opts_ + num_gopt, "help") == opts_ + num_gopt) { if (num_gopt) { std::cerr << Tcl_GetString(argv[0]); for (int oi = 0; oi < num_gopt; ++oi) { @@ -1832,9 +2273,7 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int if (num_gopt) { for (; ai < argc; ++ai) { - //if (argv[ai]->typePtr) break; char * s = Tcl_GetString(argv[ai]); - //std::cerr << "look at: " << s << "\n"; if (s[0] == '-') { if (s[1] == '-' && s[2] == 0) { ++ai; @@ -1844,7 +2283,6 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int for (int oi = 0; oi < num_gopt; ++oi) { if (opts_[oi].compare(s + 1) == 0) { found = true; - //std::cerr << s << " at " << ai << " mapped to " << oi << " has_arg " << has_arg_[oi] << "\n"; if (has_arg_[oi]) { ++ai; if (ai >= argc) { @@ -1863,54 +2301,133 @@ void callback_v::invoke(void * pv, Tcl_Interp * interp, int break; } } + if (may_have_defaults) { + for (int oi = 0; oi < num_gopt; ++oi) { + if (! getopt_argv[oi]) { + if (!defaults_[oi].empty()) { + Tcl_IncrRefCount(getopt_argv[oi] = Tcl_NewStringObj(defaults_[oi].c_str(), defaults_[oi].size())); + getopt_allocated[oi] = true; + any_getopt_allocated |= true; + } + } + } + } } check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ || policies_.variadic_ ? 1 : 0) - num_gopt - num_opt, has_variadic_ || policies_.variadic_ ? -1 : sizeof...(Ts) - num_gopt, policies_.usage_); + + auto dealloc = [&](void *) { + if (may_have_defaults && any_getopt_allocated) { + for (int oi = 0; oi < num_gopt; ++oi) { + if (getopt_allocated[oi]) { + Tcl_DecrRefCount(getopt_argv[oi]); + } + } + } + }; + std::unique_ptr dealloc_raii(0, dealloc); + + do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + if (pol_t::postproc) { + post_process_policies(interp, policies_, argv + ai, false); + } +} +template +int callback_v::callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + this_t * this_p = (this_t *) cd; + + return this_p->checked_invoke_impl(nullptr, interp, objc, objv, false); +} + +template +void overloaded_callback::invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { + do_invoke<0>(pv, interp, argc, argv, object_dot_method); +} + +template +int overloaded_callback::checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { try { - do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + invoke_impl(pv, interp, argc, argv, object_dot_method); } catch (tcl_usage_message_printed & e) { } catch (std::exception & e) { for (int i = 0; i < argc; ++i) { if (i) { std::cerr << " "; } - if (argv[i]->typePtr && strcmp(argv[i]->typePtr->name, "list") == 0) { + if (interpreter_->is_list(argv[i])) { std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; } else { std::cerr << Tcl_GetString(argv[i]); } } std::cerr << ": " << e.what() << "\n"; - return; - } catch (...) {//std::exception & e) { - throw; + return TCL_ERROR; + } catch (...) { + std::cerr << "Unknown error.\n"; + return TCL_ERROR; } - post_process_policies(interp, policies_, argv + ai, false); + return TCL_OK; +} +template +int overloaded_callback::callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + this_t * this_p = (this_t *) cd; + + return this_p->checked_invoke_impl(nullptr, interp, objc, objv, false); +} + +template +void overloaded_callback::install() { + Tcl_CreateObjCommand(interpreter_->get_interp(), name_.c_str(), callback_handler, (ClientData) this, 0); +} +template +void overloaded_callback::uninstall() { + Tcl_DeleteCommand(interpreter_->get_interp(), name_.c_str()); } } +#undef COLD #include "cpptcl/cpptcl_object.h" -#ifdef CPPTCL_MANAGED_CLASS template object interpreter::call_managed_constructor::operator()(Ts... ts) { object ret = interp_->makeobj(new C(ts...), true, [this](C * p) { std::ostringstream oss; - oss << "p" << p << "."; + oss << object_namespace_name << "::" << p << "."; Tcl_DeleteCommand(interp_->get_interp(), oss.str().c_str()); - //std::cerr << oss.str() << " removed\n"; delete p; }); - if (false) { - std::cerr << "managed_create " << ret.get_object() - << " " << ret.get_object()->internalRep.twoPtrValue.ptr1 - << " " << ret.get_object()->internalRep.twoPtrValue.ptr2 - << "\n"; - } return ret; } -#endif + +inline std::string interpreter::objinfo(object const & o) { + Tcl_Obj * oo = o.get_object(); + std::ostringstream oss; + oss << "obj=" << oo << " refcount=" << oo->refCount << " typePtr=" << oo->typePtr << " typename=" << (oo->typePtr ? oo->typePtr->name : "(notype)") << " ptr1=" << oo->internalRep.twoPtrValue.ptr1 << " ptr2=" << oo->internalRep.twoPtrValue.ptr2 << "\n"; + return oss.str(); +} + +template +inline object any::as_object() { + return object(this->o_); +} namespace details { -template -typename details::fix_variadic_return::type do_cast(interpreter * tcli, std::pair objecttypes, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol) { +template +struct conversion_offset { + static const int value = + (std::is_same::type, std::integral_constant >::value || + std::is_same::type, std::integral_constant >::value) + ? i + 1 : conversion_offset::value; +}; + +template +struct conversion_offset { + static const int value = -1; +}; +template +struct conversion_offset<0, 0, arg, nargs, Conv> { + static const int value = -1; +}; + +template +typename details::fix_variadic_return::type do_cast(interpreter * tcli, std::pair objecttypes, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol, Conv const & conv, C * this_p) { typedef typename remove_rc::type TT; static const bool is_variadic_arg = is_variadic::value; @@ -1925,38 +2442,50 @@ typename details::fix_variadic_return::type do_cast(interpr static const bool is_any_arg = is_any::value; static const bool is_any_list_arg = is_any_arg && all_lists::value; - + typedef typename optional_unpack::type opt_unpack_t; - //std::cerr << "do_cast argi=" << Ii << "/" << In << " argoff=" << ArgOffset << " objc=" << objc << " " << typeid(TTArg).name() << "\n"; + static const int conversion = conversion_offset<0, std::tuple_size::value, Ii + ConvOffset, In + ConvOffset, Conv>::value; + + if constexpr (conversion >= 0) { + using conv_t = typename std::tuple_element::type; + if constexpr (std::is_invocable::value) { + return std::get(conv)(objv[Ii]); + } else if constexpr (std::is_invocable::value) { + return std::get(conv)(this_p, objc, objv, ArgOffset + Ii); + } else if constexpr (std::is_invocable::value) { + return std::get(conv)(objc, objv, ArgOffset + Ii); + } else if constexpr (std::is_invocable::value) { + //return std::get(conv)(do_cast<0, ConvOffset, opt_unpack_t, In, Ii>(tcli, objecttypes, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol, std::tuple<>(), this_p)); + } else { + static_assert(Conv::no_such_member); + std::cerr << "_Z" << typeid(this_p).name() << "\n"; + throw tcl_error("conversion function has incompatible signature. this ought to be a compile time error"); + } + } + if constexpr (is_old_variadic_arg) { if (pol.variadic_) { return details::get_var_params(tcli->get(), objc, objv, Ii + ArgOffset, pol); } } - + if constexpr (is_optional_arg || is_getopt_arg) { if constexpr (is_getopt_arg) { static const int getopti = getopt_index<0, Ii, 0, Ts...>::value; - //std::cerr << "getopti " << getopti << " " << getoptv[getopti] << "\n"; if (getoptv[getopti]) { if constexpr (std::is_same >::value) { - //std::cerr << "truebool\n"; return TT(true, true); } else { - //std::cerr << "optional\n"; - return TT(do_cast<0, opt_unpack_t, In, Ii>(tcli, objecttypes, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol), true); - //return TT(tcli->template tcl_cast(interp, getoptv[getopti], cpptcl_byref), true); + return TT(do_cast<0, ConvOffset, opt_unpack_t, In, Ii>(tcli, objecttypes, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol, conv, this_p), true); } } else { - //std::cerr << "falsebool\n"; return TT(opt_unpack_t(), false); } } else { if (objc > Ii + ArgOffset) { - return TT(do_cast(tcli, objecttypes, pack, variadic, interp, objc, objv, getoptc, getoptv, pol), true); - //return { tcli->template tcl_cast(interp, objv[Ii + ArgOffset], cpptcl_byref), true }; + return TT(do_cast(tcli, objecttypes, pack, variadic, interp, objc, objv, getoptc, getoptv, pol, conv, this_p), true); } else { return { typename std::decay::type(), false }; } @@ -1964,24 +2493,18 @@ typename details::fix_variadic_return::type do_cast(interpr } else if constexpr (is_list_arg) { if (objv[Ii + ArgOffset]->typePtr) { Tcl_ObjType * ot = tcli->get_objtype(); - if (strcmp(objv[Ii + ArgOffset]->typePtr->name, "list") == 0) { + if (tcli->is_list(objv[Ii + ArgOffset])) { return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); - //ot return TT(tcli, objv[Ii + ArgOffset], ot); - //return return_dummy_value::doit(tcli, objv[Ii + ArgOffset], ot); } else { Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); - //std::cerr << "create listone " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << (ot ? ot->name : "") << "\n"; return TT(tcli, lo, objecttypes.first, true); - //ot return TT(tcli, lo, ot, true); - //return return_dummy_value::doit(tcli, lo, ot); } } else { return TT(nullptr, nullptr, nullptr); - //throw tcl_error("expecting object at index" + std::to_string(Ii + ArgOffset)); } } else if constexpr (is_any_arg) { if (objv[Ii + ArgOffset]->typePtr) { - if (strcmp(objv[Ii + ArgOffset]->typePtr->name, "list") == 0) { + if (tcli->is_list(objv[Ii + ArgOffset])) {//->typePtr == list_type_) { if constexpr (is_any_list_arg) { int len; if (Tcl_ListObjLength(interp, objv[Ii + ArgOffset], &len) == TCL_OK) { @@ -2013,27 +2536,17 @@ typename details::fix_variadic_return::type do_cast(interpr return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); } } - //return return_dummy_value::doit(ret.set(tcli, objv[Ii + ArgOffset])); } else { if constexpr (is_any_list_arg) { return TT(nullptr, nullptr, nullptr); } else { throw tcl_error("any<> argument does not map to an object"); } - //return return_dummy_value::doit(interp, nullptr); - //return TT(nullptr, nullptr); } } else { if constexpr (is_variadic_arg) { - //return details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol); - //return details::return_dummy_value::doit(details::get_var_params(interp, objc, objv, In + ArgOffset - 1, pol)); return TT(tcli, objc, objv); } else { - //} else { - //return details::tcl_cast::from(interp, objv[Ii + ArgOffset], cpptcl_byref); - //} - //std::cerr << "regular arg " << Tcl_GetString(objv[Ii + ArgOffset]) << " " << objv[Ii + ArgOffset] << " " << Ii << " " << ArgOffset << " " << objv[Ii + ArgOffset]->typePtr << "[" << (objv[Ii + ArgOffset]->typePtr ? objv[Ii + ArgOffset]->typePtr->name : "") << "]\n"; - //return details::tcl_cast::from(interp, objv[Ii + ArgOffset], false); if constexpr (std::is_pointer::value && std::is_class::type>::value) { return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false, *objecttypes.first); } else { @@ -2054,9 +2567,9 @@ void interpreter::defvar(std::string const & name, T & v) { }); } -template +template template -interpreter::function_definer & interpreter::function_definer::defvar(std::string const & name, T C::*t) { +interpreter::function_definer & interpreter::function_definer::defvar(std::string const & name, T C::*t) { return def(name, [t] (opt const & arg) -> T { if (arg) { *t = arg; @@ -2065,31 +2578,88 @@ interpreter::function_definer & interpreter::function_definer::defvar(std: }); } -template -template -interpreter::class_definer & interpreter::class_definer::defvar(std::string const & name, T C::*varp) { - return this->def(name, [varp] (C * this_p, opt const & val) -> T { +template +template +interpreter::class_definer & interpreter::class_definer::defvar(std::string const & name, T CC::*varp) { + return this->def(name, [varp] (CC * this_p, opt const & val) -> T { if (val) this_p->*varp = *val; return this_p->*varp; }); } -inline void interpreter::setVar(std::string const & name, object const & value) { - object name_obj(name); +template +template +interpreter::function_definer & interpreter::function_definer::def2(std::string const & name, Fn fn, std::index_sequence, Extra... extra) { + if constexpr (std::is_same::value) { + interp_->def(name, fn, std::get(with_extra)..., extra...); + } else { + interp_->def(name, fn, this_p_, std::get(with_extra)..., extra...); + } + return *this; +} -#if 0 - Tcl_Obj * ro2 = Tcl_ObjGetVar2(interp_, name_obj.get_object(), nullptr, 0); - Tcl_Obj * ro; - { - object value2 = value.duplicate(); - std::cerr << "sharedo1: " << value.shared() << " " << name_obj.shared() << " " << value.get_object()->refCount << " " << value2.get_object() << " " << ro2 << " " << (ro2 ? ro2->refCount : -22) << "\n"; - ro = Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, value2.get_object(), 0); +template +template +interpreter::class_definer & interpreter::class_definer::def2(std::string const & name, Fn f, std::index_sequence, Extra... extra) { + const bool epol = std::tuple_size::value != sizeof...(extra); + auto pol = epol ? eap(extra...) : eap(std::get(with_extra)...); + + auto convtu = std::tuple_cat(eac(extra...), eac(std::get(with_extra)...)); + auto wrapped = eaw(f, std::get(with_extra)...); + + typedef typename details::attributes_merge::type attrs; + + ch_->register_method(name, new callback_t::value || details::has_policies::value, attrs> + (interp_, wrapped, name, pol, convtu), + interp_->get_interp(), managed_); + return *this; +} + +template +template +interpreter::class_definer & interpreter::class_definer::def2(std::string const & name, overloaded const & over, std::index_sequence seq, Extra... extra) { + const bool epol = std::tuple_size::value != sizeof...(extra); + auto convtu = std::tuple_cat(eac(extra...), eac(std::get(with_extra)...)); + + typedef typename details::attributes_merge::type attrs; + + ch_->register_method(name, new details::overloaded_callback, decltype(convtu), Over...> + (interp_, over, name, epol ? eap(extra...) : eap(std::get(with_extra)...), convtu), + interp_->get_interp(), managed_); + return *this; +} + +template +interpreter::class_definer interpreter::class_(std::string const &name, init const & init_arg, Extra... extra) { + typedef callback_t, C * (*)(Ts...), true> callback_type; + + details::class_handler * ch = get_class_handler(name); + if (ch && ! init_arg.default_) { + throw tcl_error("overloaded constructors not implemented"); } + if (!ch) { + ch = new details::class_handler(); + add_class(name, ch); + add_constructor(name, ch, new callback_type(this, &call_constructor::doit, name, extra...)); + } + return class_definer(ch, this, false, extra...); +} + +template +interpreter::class_definer interpreter::class_(std::string const &name, details::no_init_type const &, Extra... extra) { + details::class_handler * ch = static_cast *>(get_class_handler(name)); + if (! ch) { + ch = new details::class_handler(); + add_class(name, ch); + } + return class_definer(ch, this, false, extra...); +} + + +inline void interpreter::setVar(std::string const & name, object const & value) { + object name_obj(name); - std::cerr << "sharedo2: " << value.shared() << " " << name_obj.shared() << " " << ro << " " << ro2 << " " << value.get_object() << " " << ro->refCount << " " << value.get_object()->refCount << "\n"; -#else Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, value.get_object(), 0); -#endif } template @@ -2097,15 +2667,12 @@ void interpreter::setVar(std::string const & name, T const & value) { object val_obj(value); object name_obj(name); - std::cerr << "shared1: " << val_obj.shared() << " " << name_obj.shared() << "\n"; Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, val_obj.get_object(), 0); - std::cerr << "shared2: " << val_obj.shared() << " " << name_obj.shared() << "\n"; } template void interpreter::makeobj_inplace(OT * n, object & obj, bool owning, DEL delfn) { - Tcl_Obj * o = obj.get_object(); //Tcl_NewObj(); - //Tcl_Obj * o = Tcl_NewObj(); + Tcl_Obj * o = obj.get_object(); if (Tcl_IsShared(o)) { throw tcl_error("cannot modify shared object in-place"); @@ -2119,7 +2686,6 @@ void interpreter::makeobj_inplace(OT * n, object & obj, bool owning, DEL delfn) } else { o->internalRep.twoPtrValue.ptr2 = new interpreter::shared_ptr_deleter(n, delfn); //new interpreter::dyn_deleter(); //nullptr; } - //o->internalRep.twoPtrValue.ptr2 = new interpreter::dyn_deleter(); //nullptr; } else { o->internalRep.twoPtrValue.ptr2 = nullptr; } @@ -2132,7 +2698,6 @@ void interpreter::makeobj_inplace(OT * n, object & obj, bool owning, DEL delfn) throw tcl_error("type lookup failed"); } Tcl_InvalidateStringRep(o); - //return object(o, true); } template object interpreter::makeobj(OT * p, bool owning, DELFN delfn) { @@ -2151,7 +2716,6 @@ void interpreter::setVar(std::string const & name, object & obj) { Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, obj.get_object(), 0); } - template struct Bind { object cmd_; diff --git a/cpptcl/cpptcl_object.h b/cpptcl/cpptcl_object.h index 67e99bf..f252515 100644 --- a/cpptcl/cpptcl_object.h +++ b/cpptcl/cpptcl_object.h @@ -267,18 +267,4 @@ class object { Tcl_Interp *interp_; }; - -// available specializations for object::get -#if 0 -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; -#endif - - - #endif /* CPPTCL_OBJECT_H */ diff --git a/libraries/gd.cpp b/libraries/gd.cpp new file mode 100644 index 0000000..815c973 --- /dev/null +++ b/libraries/gd.cpp @@ -0,0 +1,270 @@ +//#define USE_TCL_STUBS + +#ifndef USE_TCL_STUBS +#define CPPTCL_NO_TCL_STUBS +#endif + +#include "gd.hpp" + +namespace Tcl { namespace GD { + using namespace detail; + void init(interpreter * tcli) __attribute__((optimize("s"))) __attribute__((cold)); + + void init(interpreter * tcli) { + auto make_gdio_ctx_from_channel = [] (Tcl_Channel chan) -> gdIOCtx * { + gdIOCtx * x = new gdIOCtx; + gdio * io = new gdio(chan); + x->data = (void *) io; + gdio::gdio_install(x); + return x; + }; + auto make_gdio_ctx_from_name = [&](Tcl_Interp * i, const char * name, int wantmode) -> gdIOCtx * { + int mode; + Tcl_Channel chan = Tcl_GetChannel(i, name, &mode); + if (! chan) return nullptr; + if (! (mode & wantmode)) return nullptr; + return make_gdio_ctx_from_channel(chan); + }; + + auto conv_color = [&](Tcl_Obj * o) { + int ret; + if (Tcl_GetIntFromObj(tcli->get_interp(), o, &ret) == TCL_OK) { + return ret; + } + int slen; + const char * s = Tcl_GetStringFromObj(o, &slen); + auto cmp = [s](const char * b) { + return strcmp(s, b) == 0; + }; + if (cmp("antialiased")) return gdAntiAliased; + else if (cmp("brushed")) return gdBrushed; + else if (cmp("styled")) return gdStyled; + else if (cmp("styled_brushed")) return gdStyledBrushed; + else if (cmp("tiled")) return gdTiled; + else if (cmp("transparent")) return gdTransparent; + return 0; + }; + + using I = Tcl::interpreter; + + struct custom { + static void polygon3(GDImage * img, std::string const & filled_open, list points, int color) { + gdPoint * pp = new gdPoint[points.size() / 2]; + for (std::size_t i = 0; i < points.size() / 2; ++i) { + pp[i / 2].x = points.at(i * 2); + pp[i / 2].y = points.at(i * 2 + 1); + } + if (filled_open.empty()) { + gdImagePolygon(img->img, pp, points.size() / 2, color); + } else if (filled_open == "filled") { + gdImageFilledPolygon(img->img, pp, points.size() / 2, color); + } else if (filled_open == "open") { + gdImageOpenPolygon(img->img, pp, points.size() / 2, color); + } + } + static void polygon2(GDImage * img, list const & points, int color) { + polygon3(img, "", points, color); + } + }; + auto fromctx = [&] (auto f) { + return [&, f] (GDFactory *, const char * name) { + gdIOCtx * ctx = make_gdio_ctx_from_name(tcli->get_interp(), name, TCL_READABLE); + auto im = f(ctx); + ctx->gd_free(ctx); + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { im }); + }; + }; + auto fromdata = [](auto f) { + return [f] (GDFactory *, const char * name, object const & o) { + int size; + unsigned char * ptr = Tcl_GetByteArrayFromObj(o.get_object(), &size); + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { f(size, ptr) }); + }; + }; + auto fromnew = [](auto f) { + return [f] (GDFactory *, const char * name, int x, int y) { + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { f(x, y) }); + }; + }; + + tcli->class_("GDFactory", no_init, factory("GDImage"), lambda_wrapper(fromnew), I::cold) + .def("create", gdImageCreate) + .def("create_truecolor", gdImageCreateTrueColor); + + tcli->class_("GDFactory", no_init, factory("GDImage"), lambda_wrapper{fromctx}, I::cold) + .def("create_from_jpeg", gdImageCreateFromJpegCtx) + .def("create_from_png", gdImageCreateFromPngCtx) + .def("create_from_gif", gdImageCreateFromGifCtx) + .def("create_from_gd", gdImageCreateFromGdCtx) + .def("create_from_gd2", gdImageCreateFromGd2Ctx) + .def("create_from_wbmp", gdImageCreateFromWBMPCtx); + + tcli->class_("GDFactory", no_init, factory("GDImage"), lambda_wrapper(fromdata), I::cold) + .def("create_from_jpeg_data", gdImageCreateFromJpegPtr) + .def("create_from_png_data", gdImageCreateFromPngPtr) + .def("create_from_gif_data", gdImageCreateFromGifPtr) + .def("create_from_gd_data", gdImageCreateFromGdPtr) + .def("create_from_gd2_data", gdImageCreateFromGd2Ptr) + .def("create_from_wbmp_data", gdImageCreateFromWBMPPtr); + + tcli->class_("GDFactory", no_init, factory("GDImage"), I::cold) + .def("create_from_gd2_part", [&](GDFactory *, const char * name, const char * chan, int x, int y, int w, int h) { + gdIOCtx * ctx = make_gdio_ctx_from_name(tcli->get_interp(), chan, TCL_READABLE); + auto im = gdImageCreateFromGd2PartCtx(ctx, x, y, w, h); + ctx->gd_free(ctx); + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { im }); + }) + .def("create_from_gd2_part_data", [&](GDFactory *, const char * name, Tcl_Obj * data, int x, int y, int w, int h) { + int sz; + unsigned char * ptr = Tcl_GetByteArrayFromObj(data, &sz); + auto im = gdImageCreateFromGd2PartPtr(sz, ptr, x, y, w, h); + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { im }); + }) + .def("version", [](GDFactory *) { return object(GD_VERSION_STRING); }) + .def("create_from_xpm", [](GDFactory *, const char * name, const char * fname) { + auto im = gdImageCreateFromXpm(const_cast(fname)); + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { im }); + }) + .def("create_from_xbm", [&](GDFactory *, const char * name, const char * fh) { + FILE * file; + Tcl_GetOpenFile(tcli->get_interp(), fh, 1, 1, (ClientData *) &file); + auto im = gdImageCreateFromXbm(file); + return named_pointer_result(strcmp(name, "#auto") == 0 ? "" : name, new GDImage { im }); + }); + //.def("features",...) + + tcli->def("GDFactory_construct", [tcli](opt const & name) { + tcli->custom_construct("GDFactory", name ? *name : "GD", new GDFactory); + }); + + auto conv_imgobj = [](GDImage * this_p, int, Tcl_Obj * const *, int) { + return this_p->img; + }; + +#if 1 + tcli->class_("GDImage", no_init) + .def("pixel", overload(gdImageGetPixel, gdImageSetPixel), I::arg<3>, conv_color) + //.def("pixelrgb", overload(gdImageGetPixel, gdImageSetPixel)) + .def("polygon", overload(custom::polygon2, custom::polygon3)); +#endif + + // functions with a color argument + tcli->class_("GDImage", no_init, I::cold, I::arg<-1>, conv_color, I::arg<0>, conv_imgobj) + .def("line", gdImageLine) + .def("rectangle", gdImageRectangle) + .def("filled_rectangle", gdImageFilledRectangle) + .def("arc", gdImageArc) + //.def("filled_arc") + .def("filled_ellipse", gdImageFilledEllipse) + .def("fill_to_border", gdImageFillToBorder, I::arg<3>, conv_color) + .def("fill", gdImageFill) + .def("deallocate_color", gdImageColorDeallocate); + + // all else + tcli->class_("GDImage", no_init, I::cold, I::arg<0>, conv_imgobj) + .def("allocate_color", overload(gdImageColorAllocate, gdImageColorAllocateAlpha)) + .def("closest_color", overload(gdImageColorClosest, gdImageColorClosestAlpha)) + .def("closest_color_hwb", gdImageColorClosestHWB) + .def("exact_color", overload(gdImageColorExact, gdImageColorExactAlpha)) + .def("resolve_color", overload(gdImageColorResolve, gdImageColorResolveAlpha)) + .def("interlace", overload([](gdImagePtr img) { return gdImageGetInterlaced(img); }, gdImageInterlace)) + .def("transparent", overload([](gdImagePtr img) { return gdImageGetTransparent(img); }, gdImageColorTransparent)) + .def("set_anti_aliased", gdImageSetAntiAliased) + .def("set_anti_aliased_dont_blend", gdImageSetAntiAliasedDontBlend, I::arg<-2>, conv_color) + .def("set_thickness", gdImageSetThickness) + .def("alpha_blending", gdImageAlphaBlending) + .def("save_alpha", gdImageSaveAlpha) + .def("clip", gdImageSetClip) + .def("bounds_safe", gdImageBoundsSafe) + ; + + // functions defined as preprocessor macros + tcli->class_("GDImage", no_init, I::arg<0>, conv_imgobj) + .def("width", [](gdImagePtr img) { return gdImageSX(img); }) + .def("height", [](gdImagePtr img) { return gdImageSY(img); }) + .def("total_colors", [](gdImagePtr img) { return gdImageColorsTotal(img); }) + .def("get_alpha", [](gdImagePtr img, int c) { return gdImageAlpha(img, c); }) + .def("green_component", [](gdImagePtr img, int c) { return gdImageGreen(img, c); }) + .def("blue_component", [](gdImagePtr img, int c) { return gdImageBlue(img, c); }) + .def("red_component", [](gdImagePtr img, int c) { return gdImageRed(img, c); }) + .def("true_color", overload([](gdImagePtr, int r, int g, int b) { return gdTrueColor(r, g, b); }, + [](gdImagePtr, int r, int g, int b, int a) { return gdTrueColorAlpha(r, g, b, a); })) + ; + + auto todata0 = [](auto f) { + return [f](GDImage * this_p) { + int sz; + + unsigned char * p; + p = (unsigned char *) f(this_p->img, &sz); + return object(Tcl_NewByteArrayObj(p, sz)); + }; + }; + auto todata1 = [](auto f) { + return [f](GDImage * this_p, int i) { + int sz; + + unsigned char * p; + p = (unsigned char *) f(this_p->img, &sz, i); + return object(Tcl_NewByteArrayObj(p, sz)); + }; + }; + + tcli->class_("GDImage", no_init, I::cold) + .def("png_data", todata1(gdImagePngPtrEx)) + .def("jpeg_data", todata1(gdImageJpegPtr)) + .def("gif_data", todata0(gdImageGifPtr)) + .def("wbmp_data", todata1(gdImageWBMPPtr)) + .def("gd_data", todata0(gdImageGdPtr)); + + auto toctx_wbmp = [&](auto f) { + return [&, f](GDImage * img, const char * chan, int i) { + gdIOCtx * ctx = make_gdio_ctx_from_name(tcli->get_interp(), chan, TCL_WRITABLE); + f(img->img, i, ctx); + ctx->gd_free(ctx); + }; + }; + auto toctx_png_jpeg = [&](auto f) { + return [&, f](GDImage * img, const char * chan, int i) { + gdIOCtx * ctx = make_gdio_ctx_from_name(tcli->get_interp(), chan, TCL_WRITABLE); + f(img->img, ctx, i); + ctx->gd_free(ctx); + }; + }; + auto toctx0 = [&](auto f) { + return [&, f](GDImage * img, const char * chan) { + gdIOCtx * ctx = make_gdio_ctx_from_name(tcli->get_interp(), chan, TCL_WRITABLE); + f(img->img, ctx); + ctx->gd_free(ctx); + }; + }; + tcli->class_("GDImage", no_init, I::cold) + .def("write_png", toctx_png_jpeg(gdImagePngCtxEx)) + .def("write_jpeg", toctx_png_jpeg(gdImageJpegCtx)) + .def("write_gif", toctx0(gdImageGifCtx)) + .def("write_wbmp", toctx_wbmp(gdImageWBMPCtx)) + ; + + //.def("write_gd", toctx(gdImage + } +} } + +#if 1 +int main(int argc, char ** argv) { + using namespace Tcl; +#ifdef USE_TCL_STUBS + Tcl_Interp * tcl = Tcl_CreateInterpWithStubs("8.6", 0); +#else + Tcl_Interp * tcl = Tcl_CreateInterp(); +#endif + + interpreter tcli(tcl, true); + GD::init(&tcli); + try { + auto r = tcli.eval(argv[1]); + std::cerr << (std::string) r << "\n"; + } catch (std::exception & e) { + std::cerr << "error: " << e.what() << "\n"; + } +} +#endif diff --git a/libraries/gd.hpp b/libraries/gd.hpp new file mode 100644 index 0000000..b156378 --- /dev/null +++ b/libraries/gd.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include +#include + +namespace Tcl { + namespace GD { + namespace detail { + template + struct gdio : public Impl { + typedef Impl this_t; + using Impl::Impl; + + static int getC_(gdIOCtx * x) { + return ((this_t *) x->data)->getC(); + } + static int getBuf_(gdIOCtx * x, void * p, int n) { + return ((this_t *) x->data)->getBuf(p, n); + } + static void putC_(gdIOCtx * x, int c) { + ((this_t *) x->data)->putC(c); + } + static int putBuf_(gdIOCtx * x, const void * p, int n) { + return ((this_t *) x->data)->putBuf(p, n); + } + static int seek_(gdIOCtx * x, int o) { + return ((this_t *) x->data)->seek(o); + } + static long tell_(gdIOCtx * x) { + return ((this_t *) x->data)->tell(); + } + static void free_(gdIOCtx * x) { + delete (gdio *) x->data; + delete x; + } + static void gdio_install(gdIOCtx * x) { + x->getC = getC_; + x->getBuf = getBuf_; + x->putC = putC_; + x->putBuf = putBuf_; + x->tell = tell_; + x->seek = seek_; + x->gd_free = free_; + } + }; + struct Tcl_Channel_IO { + Tcl_Channel chan; + Tcl_Channel_IO(Tcl_Channel c) : chan(c) { } + int getC() { + char buf[2]; + Tcl_Read(chan, buf, 1); + return buf[0]; + } + void putC(int c) { + char buf[1]; + buf[0] = (char) c; + Tcl_Write(chan, buf, 1); + } + int getBuf(void * p, int n) { + return Tcl_Read(chan, (char *) p, n); + } + int putBuf(const void * p, int n) { + return Tcl_Write(chan, (const char *) p, n); + } + int seek(int o) { + return Tcl_Seek(chan, o, SEEK_SET); + } + long tell() { + return Tcl_Tell(chan); + } + }; + + struct GDFactory { + }; + struct GDImage : public interpreter::type_ops { + gdImagePtr img; + + GDImage(gdImagePtr i) : img(i) { } + }; + } + } +} From bb79de6f060792b4c1f9c685820b3295c7ab9a59 Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Wed, 8 Dec 2021 08:15:29 +0100 Subject: [PATCH 09/10] improvements * value (copy) semantics for objects, embedding in Tcl_Obj->internalRep for small types * share callback objects between interpreters * call object methods with dot notation (. $object method args) to avoid installing command handlers for each individual object --- cpptcl.cc | 348 +++++++++++------ cpptcl/cpptcl.h | 852 +++++++++++++++++++++++++++++------------ cpptcl/cpptcl_object.h | 88 +++-- 3 files changed, 897 insertions(+), 391 deletions(-) diff --git a/cpptcl.cc b/cpptcl.cc index 0905a16..ef3caab 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -27,6 +27,7 @@ typedef std::pair, policies> callback_handler_client_d struct constructor_handler_client_data_t { callback_base * cb; class_handler_base * chb; + interpreter * interp; }; struct method_handler_client_data { @@ -34,11 +35,13 @@ struct method_handler_client_data { class_handler_base * chb; bool unroll; Tcl_Interp * interp; + interpreter * tcli; }; struct managed_method_handler_client_data { void * obj; callback_base * cb; + interpreter * interp; }; result::result(Tcl_Interp *interp) : interp_(interp) {} @@ -51,7 +54,7 @@ result::operator string() const { Tcl_Obj *obj = Tcl_GetObjResult(interp_); return Tcl_GetString(obj); } -result::operator object() const { object ret(Tcl_GetObjResult(interp_)); ret.set_interp(interp_); return ret; } +result::operator object() const { object ret(Tcl_GetObjResult(interp_)); return ret; } //ret.set_interp(interp_); return ret; } void result::reset() { Tcl_ResetResult(interp_); @@ -111,7 +114,7 @@ object details::get_var_params(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv o.assign(objv[from]); } - o.set_interp(interp); + //o.set_interp(interp); return o; } @@ -126,18 +129,18 @@ callback_map callbacks; callback_map constructors; // map of call policies -typedef map policies_interp_map; -typedef map policies_map; +//typedef map policies_interp_map; +//typedef map policies_map; -policies_map call_policies; +//policies_map call_policies; -typedef std::map all_definitions2_t; -typedef std::map all_definitions_t; -all_definitions_t all_definitions; + //typedef std::map all_definitions2_t; + //typedef std::map all_definitions_t; + //all_definitions_t all_definitions; // map of object handlers -typedef map class_interp_map; -typedef map class_handlers_map; +typedef map class_handlers_map; + //typedef map class_handlers_map; class_handlers_map class_handlers; @@ -206,7 +209,7 @@ extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_O //string methodName(Tcl_GetString(objv[1])); //policies &pol = chb->get_policies(methodName); - cdd->chb->invoke(cdd->obj, interp, objc, objv, false); + cdd->chb->invoke(cdd->tcli, cdd->obj, interp, objc, objv, false); //chb->invoke(p, interp, objc, objv); //post_process_policies(interp, pol, objv, true); @@ -225,7 +228,7 @@ extern "C" int managed_method_handler(ClientData cd, Tcl_Interp * interp, int ob auto cdd = reinterpret_cast(cd); try { - cdd->cb->invoke(cdd->obj, interp, objc, objv, true); + cdd->cb->invoke(cdd->interp, cdd->obj, interp, objc, objv, true); } catch (std::exception & e) { Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); return TCL_ERROR; @@ -242,7 +245,7 @@ static void method_handler_client_data_delete(ClientData cd) { if (p->unroll) { std::ostringstream oss; oss << 'p' << p->obj; - p->chb->uninstall_methods(p->interp, oss.str().c_str()); + p->chb->uninstall_methods(p->tcli, p->interp, oss.str().c_str()); } delete p; } @@ -256,7 +259,7 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, constructor_handler_client_data_t * up = (constructor_handler_client_data_t *) cd; try { - up->cb->invoke(nullptr, interp, objc, objv, false); + up->cb->invoke(up->interp, nullptr, interp, objc, objv, false); // if everything went OK, the result is the address of the // new object in the 'pXXX' form @@ -274,6 +277,7 @@ extern "C" int constructor_handler(ClientData cd, Tcl_Interp *interp, int objc, cd->chb = up->chb; cd->unroll = false; cd->interp = nullptr; + cd->tcli = up->interp; //Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(up->chb), 0); Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(cd), method_handler_client_data_delete); @@ -322,10 +326,10 @@ extern "C" int managed_constructor_handler(ClientData cd, Tcl_Interp *interp, in } if (objv2) { - up->cb->invoke(nullptr, interp, objc, objv2, false); + up->cb->invoke(up->interp, nullptr, interp, objc, objv2, false); delete[] objv2; } else { - up->cb->invoke(nullptr, interp, objc, objv, false); + up->cb->invoke(up->interp, nullptr, interp, objc, objv, false); } //std::cerr << " managed construct " << Tcl_GetObjResult(interp)->refCount << "\n"; @@ -334,23 +338,26 @@ extern "C" int managed_constructor_handler(ClientData cd, Tcl_Interp *interp, in // new object in the 'pXXX' form // - we can create a new command with this name - string const str(Tcl_GetString(Tcl_GetObjResult(interp)) + interpreter::object_namespace_prefix_len); + //string const str(Tcl_GetString(Tcl_GetObjResult(interp)) + interpreter::object_namespace_prefix_len); - istringstream ss(str); - void *p; - ss >> p; + //istringstream ss(str); + //void *p; + //ss >> p; + Tcl_Obj * ores = Tcl_GetObjResult(interp); + method_handler_client_data * cd = new method_handler_client_data; - cd->obj = p; + void * p = cd->obj = ores->internalRep.twoPtrValue.ptr1; cd->chb = up->chb; cd->unroll = unroll; cd->interp = interp; + cd->tcli = up->interp; //Tcl_CreateObjCommand(interp, cmd.c_str(), object_handler, static_cast(up->chb), 0); if (unroll) { //std::cerr << "install methods\n"; - up->chb->install_methods(interp, Tcl_GetString(Tcl_GetObjResult(interp)), p); + up->chb->install_methods(up->interp, interp, Tcl_GetString(Tcl_GetObjResult(interp)), p); } if (! nocommand) { @@ -432,7 +439,7 @@ static void managed_method_client_data_delete(ClientData cd) { delete[] p; } -void class_handler_base::uninstall_methods(Tcl_Interp * interp, const char * prefix) { +void class_handler_base::uninstall_methods(interpreter * tcli, Tcl_Interp * interp, const char * prefix) { char buf[1024]; strcpy(buf, prefix); char * p = buf + strlen(buf); @@ -446,7 +453,7 @@ void class_handler_base::uninstall_methods(Tcl_Interp * interp, const char * pre } } -void class_handler_base::install_methods(Tcl_Interp * interp, const char * prefix, void * obj) { +void class_handler_base::install_methods(interpreter * tcli, Tcl_Interp * interp, const char * prefix, void * obj) { char buf[1024]; strcpy(buf, prefix); char * p = buf + strlen(buf); @@ -458,6 +465,7 @@ void class_handler_base::install_methods(Tcl_Interp * interp, const char * prefi strcpy(p, it->first); cds[ix].obj = obj; cds[ix].cb = it->second; + cds[ix].interp = tcli; //std::cerr << "install method " << buf << "\n"; Tcl_CreateObjCommand(interp, buf, managed_method_handler, static_cast(cds + ix), ix == 0 ? managed_method_client_data_delete : nullptr); //throw tcl_error(std::string("cannot register method ") + buf + " on object creation of managed class"); @@ -483,15 +491,24 @@ policies &class_handler_base::get_policies(string const &name) { } #endif -Tcl_Obj * object::default_object_ = nullptr; +thread_local Tcl_Obj * object::default_object_ = nullptr; -object::object() : interp_(0) { +object::object() : interp_(nullptr) { + if (! default_object_) { static_initialize(); } + obj_ = default_object_; + Tcl_IncrRefCount(obj_); +} +object::object(interpreter * interp) : interp_(interp) { if (! default_object_) { static_initialize(); } obj_ = default_object_; Tcl_IncrRefCount(obj_); } -object::object(bool b) : interp_(0) { +object::object(bool b) : interp_(nullptr) { + obj_ = Tcl_NewBooleanObj(b); + Tcl_IncrRefCount(obj_); +} +object::object(interpreter * interp, bool b) : interp_(interp) { obj_ = Tcl_NewBooleanObj(b); Tcl_IncrRefCount(obj_); } @@ -500,35 +517,60 @@ object::object(char const * buf, size_t size) : interp_(0) { obj_ = Tcl_NewByteArrayObj(reinterpret_cast(buf), static_cast(size)); Tcl_IncrRefCount(obj_); } +object::object(interpreter * interp, char const * buf, size_t size) : interp_(interp) { + obj_ = Tcl_NewByteArrayObj(reinterpret_cast(buf), static_cast(size)); + Tcl_IncrRefCount(obj_); +} object::object(double d) : interp_(0) { obj_ = Tcl_NewDoubleObj(d); Tcl_IncrRefCount(obj_); } +object::object(interpreter * interp, double d) : interp_(interp) { + obj_ = Tcl_NewDoubleObj(d); + Tcl_IncrRefCount(obj_); +} object::object(int i) : interp_(0) { obj_ = Tcl_NewIntObj(i); Tcl_IncrRefCount(obj_); } +object::object(interpreter * interp, int i) : interp_(interp) { + obj_ = Tcl_NewIntObj(i); + Tcl_IncrRefCount(obj_); +} object::object(long l) : interp_(0) { obj_ = Tcl_NewLongObj(l); Tcl_IncrRefCount(obj_); } +object::object(interpreter * interp, long l) : interp_(interp) { + obj_ = Tcl_NewLongObj(l); + Tcl_IncrRefCount(obj_); +} object::object(char const * s) : interp_(0) { obj_ = Tcl_NewStringObj(s, -1); Tcl_IncrRefCount(obj_); } +object::object(interpreter * interp, char const * s) : interp_(interp) { + obj_ = Tcl_NewStringObj(s, -1); + Tcl_IncrRefCount(obj_); +} object::object(string const & s) : interp_(0) { obj_ = Tcl_NewStringObj(s.data(), static_cast(s.size())); Tcl_IncrRefCount(obj_); } +object::object(interpreter * interp, string const & s) : interp_(interp) { + obj_ = Tcl_NewStringObj(s.data(), static_cast(s.size())); + Tcl_IncrRefCount(obj_); +} object::object(Tcl_Obj * o, bool shared) : interp_(0) { init(o, shared); } +object::object(interpreter * interp, Tcl_Obj * o, bool shared) : interp_(interp) { init(o, shared); } -object::object(object const & other, bool shared) : interp_(other.get_interp()) { init(other.obj_, shared); } +object::object(object const & other, bool shared) : interp_(other.interp_) { init(other.obj_, shared); } void object::init(Tcl_Obj * o, bool shared) { if (true || shared) { @@ -636,7 +678,7 @@ object &object::assign(Tcl_Obj *o) { object object::duplicate() const { Tcl_Obj * to = Tcl_DuplicateObj(obj_); - return object(to); + return object(interp_, to); } object &object::swap(object &other) { @@ -645,18 +687,18 @@ object &object::swap(object &other) { return *this; } -template T object::get(interpreter &i) const { - return tcl_cast::from(i.get(), obj_, false); +template T object::get() const { //interpreter &i) const { + return tcl_cast::from(interp_ ? interp_->get() : nullptr, obj_, false); } -template bool object::get(interpreter &i) const; -template double object::get(interpreter &i) const; -template float 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; +template bool object::get() const; +template double object::get() const; +template float object::get() const; +template int object::get() const; +template long object::get() const; +template char const * object::get() const; +template std::string object::get() const; +template std::vector object::get>() const; string object::asString() const { return get(); } int object::asInt() const { return get(); } @@ -677,18 +719,18 @@ bool object::is_list() const { return obj_->typePtr && strcmp(obj_->typePtr->name, "list") == 0; } -size_t object::size(interpreter &i) const { +size_t object::size() const { int len; - if (Tcl_ListObjLength(i.get(), obj_, &len) != TCL_OK) { - throw tcl_error(i.get()); + if (Tcl_ListObjLength(interp_ ? interp_->get_interp() : nullptr, obj_, &len) != TCL_OK) { + throw tcl_error(interp_->get_interp()); } return static_cast(len); } -object object::at_ref(size_t index, interpreter & i) const { +object object::at_ref(size_t index) const { Tcl_Obj *o; - if (Tcl_ListObjIndex(i.get(), obj_, static_cast(index), &o) != TCL_OK) { - throw tcl_error(i.get()); + if (Tcl_ListObjIndex(interp_ ? interp_->get_interp() : nullptr, obj_, static_cast(index), &o) != TCL_OK) { + throw tcl_error(interp_->get_interp()); } if (o == NULL) { throw tcl_error("Index out of range."); @@ -696,10 +738,10 @@ object object::at_ref(size_t index, interpreter & i) const { return object(o, true); } -object object::at(size_t index, interpreter &i) const { +object object::at(size_t index) const { Tcl_Obj *o; - if (Tcl_ListObjIndex(i.get(), obj_, static_cast(index), &o) != TCL_OK) { - throw tcl_error(i.get()); + if (Tcl_ListObjIndex(interp_ ? interp_->get_interp() : nullptr, obj_, static_cast(index), &o) != TCL_OK) { + throw tcl_error(interp_->get_interp()); } if (o == NULL) { throw tcl_error("Index out of range."); @@ -707,53 +749,52 @@ object object::at(size_t index, interpreter &i) const { return object(o); } -object &object::append(object const &o, interpreter &i) { +object &object::append(object const &o) { dupshared(); - if (Tcl_ListObjAppendElement(i.get(), obj_, o.obj_) != TCL_OK) { - throw tcl_error(i.get()); + if (Tcl_ListObjAppendElement(interp_ ? interp_->get_interp() : nullptr, obj_, o.obj_) != TCL_OK) { + throw tcl_error(interp_->get_interp()); } return *this; } -object &object::append_list(object const &o, interpreter &i) { +object &object::append_list(object const &o) { dupshared(); - if (Tcl_ListObjAppendList(i.get(), obj_, o.obj_) != TCL_OK) { - throw tcl_error(i.get()); + if (Tcl_ListObjAppendList(interp_ ? interp_->get_interp() : nullptr, obj_, o.obj_) != TCL_OK) { + throw tcl_error(interp_->get_interp()); } return *this; } -object &object::replace(size_t index, size_t count, object const &o, interpreter &i) { +object &object::replace(size_t index, size_t count, object const &o) { dupshared(); - int res = Tcl_ListObjReplace(i.get(), obj_, static_cast(index), static_cast(count), 1, &(o.obj_)); + int res = Tcl_ListObjReplace(interp_ ? interp_->get_interp() : nullptr, obj_, static_cast(index), static_cast(count), 1, &(o.obj_)); if (res != TCL_OK) { - throw tcl_error(i.get()); + throw tcl_error(interp_->get_interp()); } return *this; } -object &object::replace_list(size_t index, size_t count, object const &o, interpreter &i) { +object &object::replace_list(size_t index, size_t count, object const &o) { int objc; Tcl_Obj **objv; - int res = Tcl_ListObjGetElements(i.get(), o.obj_, &objc, &objv); + int res = Tcl_ListObjGetElements(interp_ ? interp_->get_interp() : nullptr, o.obj_, &objc, &objv); if (res != TCL_OK) { - throw tcl_error(i.get()); + throw tcl_error(interp_->get_interp()); } dupshared(); - res = Tcl_ListObjReplace(i.get(), obj_, static_cast(index), static_cast(count), objc, objv); + res = Tcl_ListObjReplace(interp_ ? interp_->get_interp() : nullptr, obj_, static_cast(index), static_cast(count), objc, objv); if (res != TCL_OK) { - throw tcl_error(i.get()); + throw tcl_error(interp_->get_interp()); } - return *this; } -void object::set_interp(Tcl_Interp *interp) { interp_ = interp; } +//void object::set_interp(Tcl_Interp *interp) { interp_ = interp; } -Tcl_Interp *object::get_interp() const { return interp_; } +//Tcl_Interp *object::get_interp() const { return interp_; } Tcl::interpreter *interpreter::defaultInterpreter = nullptr; @@ -761,6 +802,52 @@ void interpreter::unsetVar(std::string const & name) { Tcl_UnsetVar(get_interp(), name.c_str(), 0); } +details::result interpreter::upVar(std::string const & frame, std::string const & srcname, std::string const & destname) { + if (Tcl_UpVar(interp_, frame.c_str(), srcname.c_str(), destname.c_str(), 0) == TCL_OK) { + return getVar(destname); + } + return result(interp_); +} + + +int interpreter::dot_call_handler(ClientData cd, Tcl_Interp * interp, int objc, Tcl_Obj * CONST * objv) { + interpreter * this_p = (interpreter *) cd; + if (objc > 1) { + Tcl_Obj * o = objv[1]; + if (Tcl_ObjType const * ot = o->typePtr) { + if (ot[1].name) { + auto it = this_p->all_classes_by_objtype_.find(ot); + if (it != this_p->all_classes_by_objtype_.end()) { + it->second.class_handler->invoke(this_p, o->internalRep.twoPtrValue.ptr1, interp, objc - 1, objv + 1, false); + } else { + std::cerr << "primary argument is not a registered cpptcl object\n"; + return TCL_ERROR; + } + } else { + assert(ot[-1].name); + auto it = this_p->all_classes_by_objtype_.find(ot - 1); + if (it != this_p->all_classes_by_objtype_.end()) { + if (it->second.is_inline) { + it->second.class_handler->invoke(this_p, (void *) &o->internalRep, interp, objc - 1, objv + 1, false); + } else { + it->second.class_handler->invoke(this_p, o->internalRep.twoPtrValue.ptr1, interp, objc - 1, objv + 1, false); + } + } else { + std::cerr << "primary argument is not a registered cpptcl object\n"; + return TCL_ERROR; + } + } + } else { + std::cerr << "primary argument in object call is not a tcl object\n"; + return TCL_ERROR; + } + } else { + std::cerr << "object call requires at least 2 parameters\n"; + return TCL_ERROR; + } + return TCL_OK; +} + interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { interp_ = Tcl_CreateInterp(); owner_ = true; @@ -768,13 +855,16 @@ interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { throw tcl_error("expecting a single interpreter"); } find_standard_types(); + Tcl_CreateObjCommand(interp_, ".", dot_call_handler, (ClientData) this, nullptr); } interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_(nullptr), terr_(nullptr) { interp_ = interp; + + if (! interp_) { interp_ = Tcl_CreateInterp(); } owner_ = owner; if (!defaultInterpreter) { - if (Tcl_InitStubs(interp, "8.6", 0) == NULL) { + if (Tcl_InitStubs(interp_, "8.6", 0) == NULL) { throw tcl_error("Failed to initialize stubs"); } // Make a copy @@ -783,6 +873,7 @@ interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_( if (!defaultInterpreter) { defaultInterpreter = new interpreter(*this); } + Tcl_CreateObjCommand(interp_, ".", dot_call_handler, (ClientData) this, nullptr); } interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.owner_), tin_(nullptr), tout_(nullptr), terr_(nullptr), @@ -790,18 +881,15 @@ interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.ow } void interpreter::custom_construct(const char * c_name, const char * o_name, void * p) { - auto it = class_handlers.find(interp_); - if (it != class_handlers.end()) { - auto it2 = it->second.find(c_name); - if (it2 != it->second.end()) { - auto * cd = new method_handler_client_data; - cd->obj = p; - cd->chb = it2->second; - cd->unroll = false; - cd->interp = nullptr; - Tcl_CreateObjCommand(interp_, o_name, object_handler, static_cast(cd), method_handler_client_data_delete); - return; - } + auto it2 = all_classes_.find(c_name); + if (it2 != all_classes_.end()) { + auto * cd = new method_handler_client_data; + cd->obj = p; + cd->chb = it2->second.get(); + cd->unroll = false; + cd->interp = nullptr; + Tcl_CreateObjCommand(interp_, o_name, object_handler, static_cast(cd), method_handler_client_data_delete); + return; } throw tcl_error("custom construct failed"); } @@ -830,7 +918,12 @@ void interpreter::make_safe() { result interpreter::eval(string const &script) { int cc = Tcl_Eval(interp_, script.c_str()); if (cc != TCL_OK) { - throw tcl_error(interp_); + if (want_abort_) { + want_abort_ = false; + throw tcl_aborted(); + } else { + throw tcl_error(interp_); + } } return result(interp_); } @@ -843,7 +936,12 @@ result interpreter::eval(istream &s) { result interpreter::eval(object const &o) { int cc = Tcl_EvalObjEx(interp_, o.get_object(), 0); if (cc != TCL_OK) { - throw tcl_error(interp_); + if (want_abort_) { + want_abort_ = false; + throw tcl_aborted(); + } else { + throw tcl_error(interp_); + } } return result(interp_); } @@ -853,7 +951,8 @@ result interpreter::getVar(string const &variableName, string const &indexName) object i = object(indexName.c_str()); Tcl_Obj * obj = Tcl_ObjGetVar2(interp_, n.get_object(), i.get_object(), 0); if (obj == NULL) { - throw tcl_error(interp_); + std::cerr << "throw!\n"; + throw tcl_error("no such variable: " + variableName + "(" + indexName + ")"); } else { Tcl_SetObjResult(interp_, obj); } @@ -864,7 +963,7 @@ result interpreter::getVar(string const &variableName) { object n = object(variableName.c_str()); Tcl_Obj * obj = Tcl_ObjGetVar2(interp_, n.get_object(), nullptr, 0); if (obj == NULL) { - throw tcl_error(interp_); + throw tcl_error("no such variable: " + variableName); } else { Tcl_SetObjResult(interp_, obj); } @@ -905,14 +1004,11 @@ void interpreter::create_alias(string const &cmd, interpreter &targetInterp, str } void interpreter::clear_definitions(Tcl_Interp *interp) { - all_definitions_t::iterator it = all_definitions.find(interp); - if (it == all_definitions.end()) return; - for (all_definitions2_t::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { - Tcl_DeleteCommand(interp, it2->first.c_str()); - delete it2->second; + for (auto & c : all_callbacks_) { + Tcl_DeleteCommand(get_interp(), c.first.c_str()); } - all_definitions.erase(it); +#if 0 { auto it = class_handlers.find(interp); if (it != class_handlers.end()) { @@ -922,45 +1018,53 @@ void interpreter::clear_definitions(Tcl_Interp *interp) { } class_handlers.erase(interp); } +#endif } -void interpreter::add_function(string const &name, callback_base * cb) { +void interpreter::add_function(string const &name, std::shared_ptr cb) { //Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, cb, 0); - cb->install(); - all_definitions[interp_][name] = cb; + cb->install(this); + all_callbacks_.insert(std::pair(name, cb)); + //all_definitions[interp_][name] = cb; } -void interpreter::add_class(string const &name, class_handler_base * chb) { - class_handlers[interp_][name] = chb; +void interpreter::add_class(string const &name, std::shared_ptr chb, bool is_inline) { + //class_handlers[name] = chb; + all_classes_.insert(std::pair(name, chb)); + Tcl_ObjType * ot = get_objtype(name.c_str()); + if (ot) { + all_classes_by_objtype_.insert(std::pair(ot, all_classes_by_objtype_value{ chb, is_inline })); + } } details::class_handler_base * interpreter::get_class_handler(std::string const & name) { - auto it = class_handlers.find(interp_); - if (it == class_handlers.end()) return nullptr; - auto it2 = it->second.find(name); - if (it2 == it->second.end()) return nullptr; - return it2->second; + auto it2 = all_classes_.find(name); + if (it2 == all_classes_.end()) return nullptr; + return it2->second.get(); } -void interpreter::add_constructor(string const &name, class_handler_base * chb, callback_base * cb) { +void interpreter::add_constructor(string const &name, class_handler_base * chb, std::shared_ptr cb) { constructor_handler_client_data_t * up = new constructor_handler_client_data_t; - up->cb = cb; + up->cb = cb.get(); up->chb = chb; - + up->interp = this; + Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, up, 0); - all_definitions[interp_][name] = cb; + all_callbacks_.insert(std::pair(name, cb)); + // all_definitions[interp_][name] = cb; } -void interpreter::add_managed_constructor(string const &name, class_handler_base * chb, callback_base * cb) { +void interpreter::add_managed_constructor(string const &name, class_handler_base * chb, std::shared_ptr cb) { constructor_handler_client_data_t * up = new constructor_handler_client_data_t; - up->cb = cb; + up->cb = cb.get(); up->chb = chb; - + up->interp = this; + Tcl_CreateObjCommand(interp_, name.c_str(), managed_constructor_handler, up, 0); - all_definitions[interp_][name] = cb; + all_callbacks_.insert(std::pair(name, cb)); + //all_definitions[interp_][name] = cb; } - int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { int res; if (Tcl_GetIntFromObj(interp, obj, &res) != TCL_OK) { @@ -968,6 +1072,14 @@ int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { } return res; } +unsigned int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { + int res; + if (Tcl_GetIntFromObj(interp, obj, &res) != TCL_OK) { + throw tcl_error(interp); + } + return res; +} + long tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { long res; @@ -1002,7 +1114,7 @@ char const *tcl_cast::from(Tcl_Interp *, Tcl_Obj *obj, bool) { ret object tcl_cast::from(Tcl_Interp * interp, Tcl_Obj * obj, bool) { object o(obj); - o.set_interp(interp); + //o.set_interp(interp); return o; } @@ -1015,21 +1127,15 @@ namespace Tcl { // helper function for post-processing call policies // for both free functions (isMethod == false) // and class methods (isMethod == true) -void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod) { +void interpreter::post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod) { // check if it is a factory if (!pol.factory_.empty()) { - class_handlers_map::iterator it = class_handlers.find(interp); - - if (it == class_handlers.end()) { - throw tcl_error("Factory was registered for unknown class."); - } - - class_interp_map::iterator oit = it->second.find(pol.factory_); - if (oit == it->second.end()) { + auto oit = all_classes_.find(pol.factory_); + if (oit == all_classes_.end()) { throw tcl_error("Factory was registered for unknown class."); } - class_handler_base *chb = oit->second; + class_handler_base *chb = oit->second.get(); // register a new command for the object returned // by this factory function @@ -1053,6 +1159,7 @@ void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST obj cd->chb = chb; cd->unroll = false; cd->interp = nullptr; + cd->tcli = this; Tcl_CreateObjCommand(interp, Tcl_GetString(Tcl_GetObjResult(interp)), object_handler, static_cast(cd), 0); } @@ -1088,6 +1195,7 @@ tcl_error::tcl_error(Tcl_Interp * interp) : std::runtime_error(Tcl_GetString(Tcl const char * tcl_error::what() const throw() { std::ostringstream oss; oss << msg_ << "\n"; +#if 0 char ** b = backtrace_symbols(backtrace_, backtrace_size_); for (int i = 0; i < backtrace_size_; ++i) { bool done = false; @@ -1108,7 +1216,11 @@ const char * tcl_error::what() const throw() { } } free(b); +#endif return strdup(oss.str().c_str()); } + decltype(interpreter::obj_type_by_tclname_) interpreter::obj_type_by_tclname_; + decltype(interpreter::obj_type_by_cppname_) interpreter::obj_type_by_cppname_; + } diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index b0f3cdb..c769f00 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -20,6 +20,7 @@ #endif #endif +#include #include #include #include @@ -67,6 +68,9 @@ class tcl_error : public std::runtime_error { const char * what() const throw(); }; +class tcl_aborted : public std::exception { +}; + class tcl_usage_message_printed { }; @@ -105,8 +109,6 @@ policies options(std::string const & options); class interpreter; class object; -void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod); - struct named_pointer_result { std::string name; void * p; @@ -132,6 +134,7 @@ template class tcl_cast_by_reference { // the following specializations are implemented template <> struct tcl_cast { static int from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; +template <> struct tcl_cast { static unsigned int from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; template <> struct tcl_cast { static long from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; template <> struct tcl_cast { static bool from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; template <> struct tcl_cast { static double from(Tcl_Interp *, Tcl_Obj *, bool byReference = false); }; @@ -145,7 +148,8 @@ template <> struct tcl_cast > { static std::vector from( class result { public: result(Tcl_Interp *interp); - + //result(interpreter * interp); + operator bool() const; operator double() const; operator int() const; @@ -155,6 +159,7 @@ class result { void reset(); private: Tcl_Interp *interp_; + //interpreter * interp_; }; void set_result(Tcl_Interp *interp, bool b); @@ -166,6 +171,14 @@ void set_result(Tcl_Interp *interp, void *p); void set_result(Tcl_Interp *interp, object const &o); void set_result(Tcl_Interp *interp, named_pointer_result); +template ::value_type, void>::value, bool>::type = true> +void set_result(Tcl_Interp * interp, std::pair it); + +template +void set_result(Tcl_Interp * interp, std::vector const & v) { + set_result(interp, std::pair(v.begin(), v.end())); +} + // helper for checking for required number of parameters // (throws tcl_error when not met) void check_params_no(int objc, int required, int maximum, const std::string &message); @@ -178,9 +191,9 @@ class callback_base { public: virtual ~callback_base() {} - virtual void invoke(void * p, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; - virtual void install() = 0; - virtual void uninstall() = 0; + virtual void invoke(interpreter *, void * p, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) = 0; + virtual void install(interpreter *) = 0; + virtual void uninstall(interpreter *) = 0; }; // base class for object command handlers @@ -203,8 +216,8 @@ class class_handler_base : public object_cmd_base { //policies &get_policies(std::string const &name); - void install_methods(Tcl_Interp * interp, const char * prefix, void * obj); - void uninstall_methods(Tcl_Interp * interp, const char * prefix); + void install_methods(interpreter * tcli, Tcl_Interp * interp, const char * prefix, void * obj); + void uninstall_methods(interpreter * tcli, Tcl_Interp * interp, const char * prefix); ~class_handler_base() { for (auto & r : methods_) { delete r.second; @@ -230,7 +243,7 @@ class class_handler_base : public object_cmd_base { template class class_handler : public class_handler_base { public: - virtual void invoke(void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) { + virtual void invoke(interpreter * tcli, void *pv, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], bool object_dot_method) { C * p = static_cast(pv); char * methodName = Tcl_GetString(objv[1]); @@ -245,20 +258,40 @@ class class_handler : public class_handler_base { throw tcl_error("Method " + std::string(methodName) + " not found."); } - it->second->invoke(pv, interp, objc, objv, false); + it->second->invoke(tcli, pv, interp, objc, objv, false); } - void install() { } - void uninstall() { } + void install(interpreter *) { } + void uninstall(interpreter *) { } }; - } // details class interpreter; +template +struct objref { + T * val_; + Tcl_Obj * obj_; + T * operator->() { + return &val_; + } + T const * operator->() const { + return &val_; + } + T & operator*() { + return val_; + } + T const & operator*() const { + return val_; + } +}; + template struct optional { T val; bool valid; + bool is_reference_; + + bool is_reference() const { return is_reference_; } operator bool() const { return valid; } T const & operator*() const { if (!valid) { @@ -274,7 +307,7 @@ struct optional { } optional() : valid(false) { } optional(T) : valid(false) { } - optional(T t, bool v) : val(t), valid(v) { } + optional(T t, bool v, bool ref) : val(t), valid(v), is_reference_(ref) { } operator std::optional() { return valid ? std::optional(val) : std::optional(); } }; @@ -313,7 +346,6 @@ template struct is_basic_type { static const bool value = is_basic_type_impl >::value; }; - } // details template @@ -326,7 +358,6 @@ overloaded overload(Ts... ts) { return overloaded{ { ts... } }; }; - template struct list { interpreter * interp_; @@ -357,7 +388,7 @@ struct list { //list(list const & other) : ot_(other.ot_), interp_(other.interp_), lo_(other.lo_) { std::cerr << '#'; } - typedef typename std::conditional::value, T, T *>::type return_t; + typedef typename std::conditional::value || std::is_same::value, T, T *>::type return_t; operator bool() const { return interp_; @@ -385,6 +416,11 @@ struct list { return with_obj_at(ix).first; } return_t operator[](std::size_t ix) const { return at(ix); } + bool is_reference(std::size_t ix) const { + Tcl_Obj * o = obj_at(ix); + if (o->typePtr == ot_[0]) { return true; } + else if (o->typePtr == ot_[1]) { return false; } + } std::pair with_obj_at(std::size_t ix) const; Tcl_Obj * obj_at(std::size_t ix) const; }; @@ -487,19 +523,15 @@ template struct all_lists > : public all_lists_impl { }; +#if 0 template struct any_impl { Tcl_Obj * o_; - int which_; - void set_which(interpreter *, Tcl_Obj * o) { - //throw tcl_error(std::string("type ") + (o->typePtr ? o->typePtr->name : "(none)") + " is not in any<> type list"); - which_ = -1; - } }; template struct any_impl : public any_impl { - void set_which(interpreter * interp, Tcl_Obj * o); }; +#endif template struct type_at { @@ -512,30 +544,25 @@ struct type_at { } template -struct any : public details::any_impl { - typedef details::any_impl super_t; - any() { super_t::which_ = -1; } +struct any { //: public details::any_impl { + //typedef details::any_impl super_t; + //any() { super_t::which_ = -1; } interpreter * interp_; Tcl_ObjType ** ot_; bool list_owner_ = false; + Tcl_Obj * o_; - any(interpreter * i, Tcl_Obj * o, Tcl_ObjType ** ot, bool list_owner = false) : interp_(i), ot_(ot), list_owner_(list_owner) { - //this->p_ = o->internalRep.otherValuePtr; - this->o_ = o; - if (o) { - //super_t::set_which(i, o); - } + any(interpreter * i, Tcl_Obj * o, Tcl_ObjType ** ot, bool list_owner = false) : interp_(i), ot_(ot), list_owner_(list_owner), o_(o) { if (list_owner) { Tcl_IncrRefCount(this->o_); } } - any(any const & o) : interp_(o.interp_), ot_(o.ot_), list_owner_(o.list_owner_) { - this->o_ = o.o_; - this->which_ = o.which_; + any(any const & o) : interp_(o.interp_), ot_(o.ot_), list_owner_(o.list_owner_), o_(o.o_) { if (list_owner_) { Tcl_IncrRefCount(this->o_); } } + any() : o_(nullptr) { } ~any() { if (list_owner_) { Tcl_DecrRefCount(this->o_); @@ -544,12 +571,22 @@ struct any : public details::any_impl { object as_object(); + bool is_reference() const { + for (int i = 0; i < sizeof...(Ts); ++i) { + if (ot_[i] == this->o_->typePtr) { + return true; + } else if (ot_[i] + 1 == this->o_->typePtr) { + return false; + } + } + throw("is_reference called on invalid any<> object"); + } operator bool() const { + if (! o_) return false; for (int i = 0; i < sizeof...(Ts); ++i) { - if (ot_[i] == this->o_->typePtr) return true; + if (ot_[i] == this->o_->typePtr || ot_[i] + 1 == this->o_->typePtr) return true; } return false; - //return super_t::which_ != -1; } template @@ -716,8 +753,14 @@ struct visit_impl { return visit_impl::invoke(a, fs...); } }; -inline std::string tcl_typename(Tcl_Obj * o) { - return o->typePtr ? o->typePtr->name : "()"; + +inline std::string tcl_typename(Tcl_ObjType const * ot) { + std::ostringstream oss; + oss << ot; + return std::string(ot ? ot->name : "()") + "@" + oss.str(); +} +inline std::string tcl_typename(Tcl_Obj const * o) { + return tcl_typename(o->typePtr); } } @@ -864,7 +907,32 @@ struct generate_argtypes { template struct fix_variadic_return { - typedef typename std::decay::type type; + //typedef typename std::decay::type type; + typedef T type; +}; +template +struct fix_variadic_return const &, Last> { + typedef opt type; +}; +template +struct fix_variadic_return const &, Last> { + typedef getopt type; +}; +template +struct fix_variadic_return const &, Last> { + typedef list type; +}; +template +struct fix_variadic_return const &, Last> { + typedef any type; +}; +template +struct fix_variadic_return const &, Last> { + typedef variadic type; +}; +template +struct fix_variadic_return { + typedef std::string type; }; template <> @@ -903,7 +971,6 @@ struct no_init_type {}; struct no_class { }; struct no_class_lambda { }; - template struct class_lambda { }; @@ -973,7 +1040,7 @@ struct callback_free_method { }; // policy tag template struct callback_postproc { }; // policy tag -template +template struct callback_attributes { }; template @@ -981,6 +1048,9 @@ struct callback_base_type > { typedef callback_empty_base type; }; +template +struct callback_pass_interpreter { }; // policy tag + template struct attributes { static const bool cold = cold_; @@ -1002,6 +1072,7 @@ struct callback_policy_unpack { static const bool free_method = false; static const bool class_lambda = false; static const bool postproc = true; + static const bool pass_interp = false; typedef attributes<> attr; }; @@ -1017,6 +1088,7 @@ struct callback_policy_unpack > : public callback_policy template struct callback_policy_unpack > : public callback_policy_unpack { typedef A attr; + //typedef typename attributes_merge::type attr; }; template @@ -1028,10 +1100,24 @@ struct callback_policy_unpack > : public callback_policy_unpack< static const bool class_lambda = true; }; +template +struct callback_policy_unpack > : public callback_policy_unpack { + static const bool pass_interp = true; +}; + template class callback_v : public callback_base_type::type { typedef callback_v this_t; + struct client_data { + this_t * this_p; + interpreter * interp; + }; + static void cleanup_client_data(ClientData cd) { + client_data * p = (client_data *) cd; + delete p; + } + typedef callback_policy_unpack pol_t; typedef typename pol_t::type C; @@ -1050,12 +1136,12 @@ class callback_v : public callback_base_type::type { Tcl_ObjType * argument_types_all_[generate_argtypes<0, Ts...>::length + 1]; - interpreter * interpreter_; + //interpreter * interpreter_; Conv conv_; std::string name_; - static const bool cold = pol_t::attr::cold; //has_attribute::value;//false && pol_t::class_lambda; + static const bool cold = pol_t::attr::cold; public : static const int num_arguments = (std::is_same::value || pol_t::free_method ? 0 : 1) + sizeof...(Ts); @@ -1065,7 +1151,7 @@ public : #define COLD __attribute__((optimize("s"))) __attribute__((cold)) #endif - callback_v(interpreter * i, functor_type f, std::string const & name, policies const & pol = policies(), Conv conv = Conv()) COLD : interpreter_(i), policies_(pol), f_(f), conv_(conv), name_(name) { + callback_v(interpreter * i, functor_type f, std::string const & name, policies const & pol = policies(), Conv conv = Conv()) COLD : policies_(pol), f_(f), conv_(conv), name_(name) { if (num_opt) { if (pol.options_.empty()) { throw tcl_error(std::string("no getopt string supplied for function \"") + name + "\" taking getopt<> arguments"); @@ -1074,12 +1160,12 @@ public : } generate_argtypes<0, Ts...>::invoke(i, argument_types_all_); } - void install() __attribute__((optimize("s"))); - void uninstall() __attribute__((optimize("s"))); - void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { - checked_invoke_impl(pv, interp, argc, argv, object_dot_method); + void install(interpreter *) __attribute__((optimize("s"))); + void uninstall(interpreter *) __attribute__((optimize("s"))); + void invoke(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { + checked_invoke_impl(tcli, pv, interp, argc, argv, object_dot_method); } - void invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) COLD; + void invoke_impl(interpreter *, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) COLD; ~callback_v() COLD { } private : @@ -1091,11 +1177,11 @@ private : } template - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) __attribute__((optimize(cold ? "s" : "3"))); + void do_invoke(std::index_sequence const &, interpreter *, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) __attribute__((optimize(cold ? "s" : "3"))); template - void do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) __attribute__((optimize(cold ? "s" : "3"))); + void do_invoke(std::index_sequence const &, interpreter *, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) __attribute__((optimize(cold ? "s" : "3"))); - int checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + int checked_invoke_impl(interpreter *, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); static int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) __attribute__((optimize(cold ? "s" : "3"))); @@ -1103,27 +1189,49 @@ private : template template -void callback_v::do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) { +void callback_v::do_invoke(std::index_sequence const &, interpreter * tcli, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj * const getoptv[], policies const & pol, void_return) { if (argc && argv) { } - if constexpr (pol_t::class_lambda) { - details::set_result(interp, f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); - } else if constexpr (std::is_same::value || pol_t::free_method) { - details::set_result(interp, f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + + if constexpr (pol_t::pass_interp) { + if constexpr (pol_t::class_lambda) { + details::set_result(interp, f_(tcli, p, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } else if constexpr (std::is_same::value || pol_t::free_method) { + details::set_result(interp, f_(tcli, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } else { + details::set_result(interp, (p->*f_)(tcli, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } } else { - details::set_result(interp, (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + if constexpr (pol_t::class_lambda) { + details::set_result(interp, f_(p, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } else if constexpr (std::is_same::value || pol_t::free_method) { + details::set_result(interp, f_(do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } else { + details::set_result(interp, (p->*f_)(do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...)); + } } } template template -void callback_v::do_invoke(std::index_sequence const &, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { +void callback_v::do_invoke(std::index_sequence const &, interpreter * tcli, CC * p, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], int getoptc, Tcl_Obj ** getoptv, policies const & pol, void_return) { if (argc && argv && interp) { } - if constexpr (pol_t::class_lambda) { - f_(p, do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); - } else if constexpr (std::is_same::value || pol_t::free_method) { - f_(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + + if constexpr (pol_t::pass_interp) { + if constexpr (pol_t::class_lambda) { + f_(tcli, p, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } else if constexpr (std::is_same::value || pol_t::free_method) { + f_(tcli, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } else { + (p->*f_)(tcli, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } + } else { + if constexpr (pol_t::class_lambda) { + f_(p, do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } else if constexpr (std::is_same::value || pol_t::free_method) { + f_(do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); } else { - (p->*f_)(do_cast::type, sizeof...(Is), Is>(interpreter_, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + (p->*f_)(do_cast(tcli, args(), ppack(), pol.variadic_, interp, argc, argv, getoptc, getoptv, pol, conv_, p)...); + } } } @@ -1136,16 +1244,28 @@ template ::value>::type> { typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; }; +template +struct callback_expand_f::value>::type> { + typedef callback_v, Attr>, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; template struct callback_expand_f::value>::type> { typedef callback_v, no_base>, postproc>, Conv, Fn, R, Ts...> type; }; +template +struct callback_expand_f::value>::type> { + typedef callback_v, Attr>, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; template struct callback_expand_f, postproc, no_base, Attr> { typedef callback_v, no_base>, postproc>, Conv, Fn, R, Ts...> type; }; +template +struct callback_expand_f, postproc, no_base, Attr> { + typedef callback_v, Attr>, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; template struct senti { }; @@ -1155,6 +1275,7 @@ struct callback_expand_f2 { typedef typename senti::type type; }; +// 6 definitions without pass_interpreter template struct callback_expand_f2 { typedef callback_v, no_base>, postproc> >, Conv, Fn, R, Ts...> type; @@ -1180,6 +1301,39 @@ struct callback_expand_f2, no_base>, postproc>, Conv, Fn, R, Ts...> type; }; +// same 6 definitions with pass_interpreter +template +struct callback_expand_f2 { + typedef callback_v, Attr>, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, Attr>, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, Attr>, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, Attr>, no_base>, postproc> >, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, Attr>, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; +template +struct callback_expand_f2 { + typedef callback_v, Attr>, no_base>, postproc>, Conv, Fn, R, Ts...> type; +}; + + + + + + + + template struct is_std_function { static const bool value = false; @@ -1219,8 +1373,17 @@ struct overloaded_callback : public callback_base_type::type { typedef typename pol_t::type Cparm_out; static const bool cold = pol_t::attr::cold; + + struct client_data { + this_t * this_p; + interpreter * interp; + }; + static void cleanup_client_data(ClientData cd) { + client_data * p = (client_data *) cd; + delete p; + } - interpreter * interpreter_; + //interpreter * interpreter_; std::string name_; std::tuple>::type...> callbacks_; @@ -1231,30 +1394,30 @@ struct overloaded_callback : public callback_base_type::type { template overloaded_callback(std::index_sequence const &, interpreter * i, overloaded f, std::string const & name, policies const & pol, Conv conv) - : interpreter_(i), name_(name), callbacks_(typename callback_expand_f>::type(i, std::get(f.ts), name, pol, conv)...) { } + : name_(name), callbacks_(typename callback_expand_f>::type(i, std::get(f.ts), name, pol, conv)...) { } template - void do_invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + void do_invoke(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); static int callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) __attribute__((optimize(cold ? "s" : "3"))); - int checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); - void invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); - void invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { - checked_invoke_impl(pv, interp, argc, argv, object_dot_method); + int checked_invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + void invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) __attribute__((optimize(cold ? "s" : "3"))); + void invoke(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { + checked_invoke_impl(tcli, pv, interp, argc, argv, object_dot_method); } - void install() COLD; - void uninstall() COLD; + void install(interpreter * tcli) COLD; + void uninstall(interpreter * tcli) COLD; }; template template -void overloaded_callback::do_invoke(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { +void overloaded_callback::do_invoke(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { if (std::tuple_element::type::num_arguments + 1 == argc) { - std::get(callbacks_).invoke_impl(pv, interp, argc, argv, object_dot_method); + std::get(callbacks_).invoke_impl(tcli, pv, interp, argc, argv, object_dot_method); return; } if constexpr (ti + 1 < std::tuple_size::value) { - do_invoke(pv, interp, argc, argv, object_dot_method); + do_invoke(tcli, pv, interp, argc, argv, object_dot_method); } else { std::cerr << "wrong number of arguments, overload accepts any of:"; overload_enumerate<0>([&] (auto * o) { @@ -1369,6 +1532,11 @@ class interpreter { } return defaultInterpreter; } + void post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_Obj *CONST objv[], bool isMethod); + bool want_abort_ = false; + void want_abort() { + want_abort_ = true; + } private: interpreter(const interpreter &i); interpreter(); @@ -1558,6 +1726,36 @@ class interpreter { return terr_; } + typedef std::map > all_callbacks_t; + all_callbacks_t all_callbacks_; + typedef std::map > all_classes_t; + all_classes_t all_classes_; + + struct all_classes_by_objtype_value { + std::shared_ptr class_handler; + bool is_inline; + }; + + typedef std::map all_classes_by_objtype_t; + all_classes_by_objtype_t all_classes_by_objtype_; + + static int dot_call_handler(ClientData cd, Tcl_Interp * interp, int objc, Tcl_Obj * CONST * objv); + + std::shared_ptr find_callback(std::string const & name) { + auto it = all_callbacks_.find(name); + if (it != all_callbacks_.end()) { + return std::shared_ptr(it->second); + } + return std::shared_ptr(nullptr); + } + std::shared_ptr find_class(std::string const & name) { + auto it = all_classes_.find(name); + if (it != all_classes_.end()) { + return std::shared_ptr(it->second); + } + return std::shared_ptr(nullptr); + } + void trace(bool v) { trace_ = v; } @@ -1662,11 +1860,28 @@ class interpreter { static constexpr const char * object_command_namespace_prefix = "O::"; static const int object_command_namespace_prefix_len = 3; #endif + +#if 0 + object make_object(long i) { return object(this, i); } + object make_object(char const *s) { return object(this, s); } + object make_object(std::string const &s) { return object(this, s); } + object make_object(Tcl_Obj *o) { return object(this, o); } + object make_object(bool b) { return object(this, b); } + object make_object(char const *buf, size_t size) { return object(this, buf, size); } + object make_object(double b) { return object(this, b); } + object make_object(int i) { return object(this, i); } + object make_object() { return object(this); } +#endif void find_standard_types(); interpreter(Tcl_Interp *, bool owner = false); ~interpreter(); + interpreter * master_ = nullptr; + void set_master(interpreter * master) { + master_ = master; + } + void make_safe(); void custom_construct(const char * classname, const char * objname, void * p); @@ -1684,8 +1899,12 @@ class interpreter { } template void def2(std::string const & name, Fn fn, Extra... extra) { - typedef decltype(details::extra_args_conversions(extra...)) conv_t; - add_function(name, new callback_t(this, fn, name, eap(extra...), eac(extra...))); + if (master_) { + add_function(name, master_->find_callback(name)); + } else { + typedef decltype(details::extra_args_conversions(extra...)) conv_t; + add_function(name, std::shared_ptr(new callback_t(this, fn, name, eap(extra...), eac(extra...)))); + } } template @@ -1695,8 +1914,12 @@ class interpreter { template void def(std::string const & name, overloaded over, Extra... extra) { - typedef decltype(details::extra_args_conversions(extra...)) conv_t; - add_function(name, new details::overloaded_callback(this, over, name, eap(extra...), eac(extra...))); + if (master_) { + add_function(name, master_->find_callback(name)); + } else { + typedef decltype(details::extra_args_conversions(extra...)) conv_t; + add_function(name, std::shared_ptr(new details::overloaded_callback(this, over, name, eap(extra...), eac(extra...)))); + } } template @@ -1719,10 +1942,9 @@ class interpreter { template class_definer class_(std::string const &name, init const & init_arg = init(details::init_default_tag()), Extra... extra) COLD; - template class_definer class_(std::string const &name, details::no_init_type const &, Extra... extra) COLD; - + template struct call_managed_constructor { interpreter * interp_; call_managed_constructor(interpreter * i) : interp_(i) { } @@ -1730,37 +1952,56 @@ class interpreter { object operator()(Ts... ts); }; - template + template class_definer managed_class_(std::string const &name, init const & = init(), Extra... extra) { - typedef details::callback_v, call_managed_constructor, object, Ts...> callback_type; + if (master_) { + std::shared_ptr chp = master_->find_class(name); + assert(chp); + add_class(name, chp, sizeof(C) <= sizeof(((Tcl_Obj *) 0)->internalRep)); + add_managed_constructor(name, chp.get(), master_->find_callback(name)); + details::class_handler * ch = static_cast *>(chp.get()); + return class_definer(ch, this, true, extra...); + } else { + typedef details::callback_v, call_managed_constructor, object, Ts...> callback_type; - if (get_class_handler(name)) { - throw tcl_error("overloaded constructors not implemented"); + if (get_class_handler(name)) { + throw tcl_error("overloaded constructors not implemented"); + } + + details::class_handler * ch = new details::class_handler(); + + this->type, ValueType >(name, (C *) 0); + + add_class(name, std::shared_ptr(ch), sizeof(C) <= sizeof(((Tcl_Obj *) 0)->internalRep)); + + add_managed_constructor(name, ch, std::shared_ptr(new callback_type(this, call_managed_constructor(this), name))); + + return class_definer(ch, this, true, extra...); } - - details::class_handler * ch = new details::class_handler(); - - this->type >(name, (C *) 0); - - add_class(name, ch); - - add_managed_constructor(name, ch, new callback_type(this, call_managed_constructor(this), name)); - - return class_definer(ch, this, true, extra...); } - template + template class_definer managed_class_(std::string const &name, details::no_init_type const &, Extra... extra) { - details::class_handler * ch = static_cast *>(get_class_handler(name)); - if (!ch) { - ch = new details::class_handler(); - add_class(name, ch); + if (master_) { + std::shared_ptr chp = master_->find_class(name); + if (all_classes_.count(name) == 0) { + add_class(name, chp, sizeof(C) <= sizeof(((Tcl_Obj *) 0)->internalRep)); + } + details::class_handler * ch = static_cast *>(chp.get()); + return class_definer(ch, this, true, extra...); + } else { + details::class_handler * ch = static_cast *>(get_class_handler(name)); + if (!ch) { + ch = new details::class_handler(); + this->type, ValueType >(name, (C *) 0); + add_class(name, std::shared_ptr(ch), sizeof(C) <= sizeof(((Tcl_Obj *) 0)->internalRep)); + } + return class_definer(ch, this, true, extra...); } - return class_definer(ch, this, true, extra...); } - std::map obj_type_by_tclname_; - std::map obj_type_by_cppname_; + static std::map obj_type_by_tclname_; + static std::map obj_type_by_cppname_; template struct deleter { @@ -1819,6 +2060,17 @@ class interpreter { } dst->typePtr = src->typePtr; } + static void dup_v(Tcl_Obj * src, Tcl_Obj * dst) { + if constexpr (sizeof(T) <= sizeof(dst->internalRep)) { + memcpy(&dst->internalRep, &src->internalRep, sizeof(T)); + } else { + if constexpr (std::is_copy_constructible::value) { + dst->internalRep.twoPtrValue.ptr1 = new T(* ((T *) src->internalRep.twoPtrValue.ptr1)); + } else { + throw tcl_error("attempting to make value for class with no copy constructor"); + } + } + } static void free(Tcl_Obj * o) { if (o->internalRep.twoPtrValue.ptr2) { deleter * d = (deleter *) o->internalRep.twoPtrValue.ptr2; @@ -1827,11 +2079,29 @@ class interpreter { } } } + static void free_v(Tcl_Obj * o) { + if constexpr (sizeof(T) <= sizeof(o->internalRep)) { + ((T *) &o->internalRep)->~T(); + } else { + delete ((T *) o->internalRep.twoPtrValue.ptr1); + } + } static void str(Tcl_Obj * o) { std::ostringstream oss; oss << (Managed ? object_namespace_name : object_namespace_name) << "::" << o->internalRep.twoPtrValue.ptr1; str_impl(o, oss.str()); } + static void str_v(Tcl_Obj * o) { + if constexpr (sizeof(T) <= sizeof(o->internalRep)) { + std::ostringstream oss; + oss << &o->internalRep; + str_impl(o, oss.str()); + } else { + std::ostringstream oss; + oss << o->internalRep.twoPtrValue.ptr1; + str_impl(o, oss.str()); + } + } static void str_impl(Tcl_Obj * o, std::string const & name) { o->bytes = Tcl_Alloc(name.size() + 1); strncpy(o->bytes, name.c_str(), name.size()); @@ -1841,6 +2111,9 @@ class interpreter { static int set(Tcl_Interp *, Tcl_Obj *) { return TCL_OK; } + static int set_v(Tcl_Interp *, Tcl_Obj *) { + return TCL_OK; + } }; template @@ -1867,33 +2140,58 @@ class interpreter { } return nullptr; } + + struct extra_typeinfo { + bool value_type; + }; - template - void type(std::string const & name, TY * p) { + template + static void type(std::string const & name, TY * p) { auto it = obj_type_by_cppname_.find(std::type_index(typeid(TY))); if (it != obj_type_by_cppname_.end()) { - std::cerr << "attempt to register type " << name << " twice\n"; + //std::cerr << "attempt to register type " << name << " twice\n"; return; } auto it2 = obj_type_by_tclname_.find(name); if (it2 != obj_type_by_tclname_.end()) { - std::cerr << "attempt to register type " << name << " twice\n"; + //std::cerr << "attempt to register type " << name << " twice\n"; + return; + } + Tcl_ObjType * ot = new Tcl_ObjType[4]; + + if constexpr (ValueType) { + char * cp_vt = new char[name.size() + 3]; + strcpy(cp_vt, name.c_str()); + cp_vt[name.size()] = '_'; + cp_vt[name.size() + 1] = 'v'; + cp_vt[name.size() + 2] = 0; + ot[2].name = cp_vt; + ot[2].freeIntRepProc = &OPS::free_v; + ot[2].dupIntRepProc = &OPS::dup_v; + ot[2].updateStringProc = &OPS::str_v; + ot[2].setFromAnyProc = &OPS::set_v; + Tcl_RegisterObjType(ot + 2); + } else { + memset(ot + 2, 0, sizeof(*ot)); + ot[2].name = "unused"; } - Tcl_ObjType * ot = new Tcl_ObjType; + //std::type_index * tip = (std::type_index *) (cp + 256); + //*tip = std::type_index(typeid(TY)); + memset(ot, 0, sizeof(*ot)); + memset(ot + 3, 0, sizeof(*ot)); - char * cp = new char[256 + sizeof(std::type_index)]; + char * cp = new char[name.size() + 1]; strcpy(cp, name.c_str()); cp[name.size()] = 0; - std::type_index * tip = (std::type_index *) (cp + 256); - *tip = std::type_index(typeid(TY)); - ot->name = cp; - ot->freeIntRepProc = &OPS::free; - ot->dupIntRepProc = &OPS::dup; - ot->updateStringProc = &OPS::str; - ot->setFromAnyProc = &OPS::set; - obj_type_by_tclname_.insert(std::pair(name, ot)); - obj_type_by_cppname_.insert(std::pair(std::type_index(typeid(TY)), ot)); - Tcl_RegisterObjType(ot); + ot[1].name = cp; + ot[1].freeIntRepProc = &OPS::free; + ot[1].dupIntRepProc = &OPS::dup; + ot[1].updateStringProc = &OPS::str; + ot[1].setFromAnyProc = &OPS::set; + + Tcl_RegisterObjType(ot + 1); + obj_type_by_tclname_.insert(std::pair(name, ot + 1)); + obj_type_by_cppname_.insert(std::pair(std::type_index(typeid(TY)), ot + 1)); } template ::type>::value, bool>::type = true> @@ -1932,6 +2230,11 @@ class interpreter { } throw tcl_error("function argument type is not registered"); } + + template ::value, bool>::type = true> + T tcl_cast(Tcl_Interp * interp, Tcl_Obj * o, bool byref) { + return *tcl_cast::type *>(interp, o, byref); + } template ::value, bool>::type = true> T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref, Tcl_ObjType * ot) { @@ -1939,6 +2242,12 @@ class interpreter { Tcl_ObjType * otp = ot; if (otp == o->typePtr) { return (T) o->internalRep.twoPtrValue.ptr1;//otherValuePtr; + } else if (otp + 1 == o->typePtr) { + if (sizeof(typename std::remove_pointer::type) <= sizeof(o->internalRep)) { + return (T) &o->internalRep; + } else { + return (T) o->internalRep.twoPtrValue.ptr1; + } } else if (is_list(o)) { int len; if (Tcl_ListObjLength(i, o, &len) == TCL_OK) { @@ -1956,14 +2265,14 @@ class interpreter { throw tcl_error("Unknown tcl error"); } } else { - throw tcl_error("function argument has wrong type"); + throw tcl_error("function argument has wrong type. expect " + details::tcl_typename(otp) + ", got " + details::tcl_typename(o->typePtr)); } } else { throw tcl_error("function argument is not a tcl object type"); } return nullptr; } - template ::value && std::is_same::type>::value && !details::is_any::value, bool>::type = true> + template ::value && !std::is_reference::value && std::is_same::type>::value && !details::is_any::value, bool>::type = true> T tcl_cast(Tcl_Interp * i, Tcl_Obj * o, bool byref) { return details::tcl_cast::from(i, o, byref); } @@ -1987,14 +2296,18 @@ class interpreter { // the InputIterator should give object& or Tcl_Obj* when dereferenced template details::result eval(InputIterator first, InputIterator last); - template object makeobj(OT * n, bool owning = false, DEL delfn = []{}); template object makeobj(OT * n, bool owning = false); template void makeobj_inplace(OT * p, object & obj, bool owning = false, DEL delfn = []{}); - + + template + void makevalue_inplace(OT * o, object & obj); + template + object makevalue(OT * o); + template void setVar(std::string const & name, T const & value); @@ -2009,6 +2322,8 @@ class interpreter { details::result getVar(std::string const &scalarTclVariable); details::result getVar(std::string const &arrayTclVariable, std::string const &arrayIndex); + details::result upVar(std::string const & frame, std::string const & srcname, std::string const & destname); + // check if variables exist bool exists(std::string const &scalarTclVariable); bool exists(std::string const &arrayTclVariable, std::string const &arrayIndex); @@ -2023,18 +2338,18 @@ class interpreter { void create_namespace(std::string const &name); // helper for cleaning up callbacks in non-managed interpreters - static void clear_definitions(Tcl_Interp *); + void clear_definitions(Tcl_Interp *); private: void operator=(const interpreter &); - void add_function(std::string const &name, details::callback_base * cb); + void add_function(std::string const &name, std::shared_ptr cb); - void add_class(std::string const &name, details::class_handler_base * chb); + void add_class(std::string const &name, std::shared_ptr chb, bool is_inline); details::class_handler_base * get_class_handler(std::string const & name); - void add_constructor(std::string const &name, details::class_handler_base * chb, details::callback_base * cb); - void add_managed_constructor(std::string const &name, details::class_handler_base * chb, details::callback_base * cb); + void add_constructor(std::string const &name, details::class_handler_base * chb, std::shared_ptr cb); + void add_managed_constructor(std::string const &name, details::class_handler_base * chb, std::shared_ptr cb); Tcl_Interp *interp_; bool owner_; @@ -2071,11 +2386,13 @@ std::pair::return_t, Tcl_Obj *> list::with_obj_at(std::size_ return ret_t(return_t(interp_, o, ot_), o); } else if constexpr (details::is_basic_type::value) { return ret_t(interp_->template tcl_cast(o), o); + } else if constexpr (std::is_same::value) { + return ret_t(object(o), o); } else { if (o->typePtr == ot_[0]) { return ret_t((return_t) o->internalRep.twoPtrValue.ptr1, o); } else { - throw tcl_error("Unexpected type in list. Got " + details::tcl_typename(o) + " need " + ot_[0]->name); + throw tcl_error("Unexpected type in list. Got " + details::tcl_typename(o) + " need " + details::tcl_typename(ot_[0])); } } } @@ -2161,34 +2478,6 @@ struct generate_argtypes { static const int length = num_subtypes::value + generate_argtypes::length; }; -template -inline void any_impl::set_which(interpreter * i, Tcl_Obj * o) { - if constexpr (is_list::value) { - //if (o->typePtr && strcmp(o->typePtr->name, "list") == 0) { - if (i->is_list(o)) { - Tcl_Obj *oo; - if (Tcl_ListObjIndex(i->get(), o, 0, &oo) == TCL_OK) { - if (i->is_type::type>(oo)) { - this->which_ = sizeof...(Ts); - return; - } - } - } else { - if (i->is_type::type>(o)) { - this->which_ = sizeof...(Ts); - return; - } - } - any_impl::set_which(i, o); - } else { - if (i->is_type(o)) { - this->which_ = sizeof...(Ts); - } else { - any_impl::set_which(i, o); - } - } -} - template struct back { typedef void type; @@ -2200,40 +2489,18 @@ struct back { }; template -void callback_v::install() { - Tcl_CreateObjCommand(interpreter_->get_interp(), name_.c_str(), callback_handler, (ClientData) this, 0); -} -template -void callback_v::uninstall() { - Tcl_DeleteCommand(interpreter_->get_interp(), name_.c_str()); -} +void callback_v::install(interpreter * tcli) { + client_data * cdata = new client_data { this, tcli }; + Tcl_CreateObjCommand(tcli->get_interp(), name_.c_str(), callback_handler, (ClientData) cdata, cleanup_client_data); +} template -int callback_v::checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { - try { - this->invoke_impl(pv, interp, argc, argv, object_dot_method); - } catch (tcl_usage_message_printed & e) { - } catch (std::exception & e) { - for (int i = 0; i < argc; ++i) { - if (i) { std::cerr << " "; } - if (this->interpreter_->is_list(argv[i])) { - std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; - } else { - std::cerr << Tcl_GetString(argv[i]); - } - } - std::cerr << ": " << e.what() << "\n"; - //Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); - return TCL_ERROR; - } catch (...) { - std::cerr << "Unknown error.\n"; - return TCL_ERROR; - } - return TCL_OK; +void callback_v::uninstall(interpreter * tcli) { + Tcl_DeleteCommand(tcli->get_interp(), name_.c_str()); } template -void callback_v::invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { +void callback_v::invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { static const int num_gopt = num_getopt::value; static const int num_opt = num_optional::value; Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; @@ -2243,10 +2510,10 @@ void callback_v::invoke_impl(void * pv, Tcl_Interp * static const bool has_variadic_ = has_variadic::value; bool any_getopt_allocated = false; - if (interpreter_->trace()) { + if (tcli->trace()) { for (int i = 0; i < argc; ++i) { std::cerr << " "; - if (interpreter_->is_list(argv[i])) { + if (tcli->is_list(argv[i])) { std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; } else { std::cerr << Tcl_GetString(argv[i]); @@ -2254,19 +2521,19 @@ void callback_v::invoke_impl(void * pv, Tcl_Interp * } std::cerr << "\n"; } - ++interpreter_->trace_count_; + ++tcli->trace_count_; if (num_gopt && argc == ai + 1 && strcmp(Tcl_GetString(argv[ai]), "-help") == 0 && std::find(opts_, opts_ + num_gopt, "help") == opts_ + num_gopt) { if (num_gopt) { std::cerr << Tcl_GetString(argv[0]); for (int oi = 0; oi < num_gopt; ++oi) { - interpreter_->terr() << " -" << opts_[oi] << (has_arg_[oi] ? " " : ""); + tcli->terr() << " -" << opts_[oi] << (has_arg_[oi] ? " " : ""); } if (policies_.variadic_) { - interpreter_->terr() << " objects..."; + tcli->terr() << " objects..."; } - interpreter_->terr() << "\n"; + tcli->terr() << "\n"; } return; } @@ -2326,32 +2593,33 @@ void callback_v::invoke_impl(void * pv, Tcl_Interp * }; std::unique_ptr dealloc_raii(0, dealloc); - do_invoke(std::make_index_sequence(), (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + do_invoke(std::make_index_sequence(), tcli, (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); if (pol_t::postproc) { - post_process_policies(interp, policies_, argv + ai, false); + tcli->post_process_policies(interp, policies_, argv + ai, false); } } template int callback_v::callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - this_t * this_p = (this_t *) cd; + client_data * cdata = (client_data *) cd; + //this_t * this_p = (this_t *) cd; - return this_p->checked_invoke_impl(nullptr, interp, objc, objv, false); + return cdata->this_p->checked_invoke_impl(cdata->interp, nullptr, interp, objc, objv, false); } template -void overloaded_callback::invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { - do_invoke<0>(pv, interp, argc, argv, object_dot_method); +void overloaded_callback::invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { + do_invoke<0>(tcli, pv, interp, argc, argv, object_dot_method); } template -int overloaded_callback::checked_invoke_impl(void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { +int overloaded_callback::checked_invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv[], bool object_dot_method) { try { - invoke_impl(pv, interp, argc, argv, object_dot_method); + invoke_impl(tcli, pv, interp, argc, argv, object_dot_method); } catch (tcl_usage_message_printed & e) { } catch (std::exception & e) { for (int i = 0; i < argc; ++i) { if (i) { std::cerr << " "; } - if (interpreter_->is_list(argv[i])) { + if (tcli->is_list(argv[i])) { std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; } else { std::cerr << Tcl_GetString(argv[i]); @@ -2367,24 +2635,36 @@ int overloaded_callback::checked_invoke_impl(void * pv, Tc } template int overloaded_callback::callback_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - this_t * this_p = (this_t *) cd; - - return this_p->checked_invoke_impl(nullptr, interp, objc, objv, false); + //this_t * this_p = (this_t *) cd; + client_data * cdata = (client_data *) cd; + + return cdata->this_p->checked_invoke_impl(cdata->interp, nullptr, interp, objc, objv, false); } template -void overloaded_callback::install() { - Tcl_CreateObjCommand(interpreter_->get_interp(), name_.c_str(), callback_handler, (ClientData) this, 0); +void overloaded_callback::install(interpreter * tcli) { + client_data * cdata = new client_data {this, tcli}; + + Tcl_CreateObjCommand(tcli->get_interp(), name_.c_str(), callback_handler, (ClientData) cdata, cleanup_client_data); } template -void overloaded_callback::uninstall() { - Tcl_DeleteCommand(interpreter_->get_interp(), name_.c_str()); +void overloaded_callback::uninstall(interpreter * tcli) { + Tcl_DeleteCommand(tcli->get_interp(), name_.c_str()); } } #undef COLD #include "cpptcl/cpptcl_object.h" +template ::value_type, void>::value, bool>::type = true> +void set_result(Tcl_Interp * interp, std::pair it) { + object o; + for (; it.first != it.second; ++it.first) { + o.append(object(*it.first)); + } + Tcl_SetObjResult(interp, o.get_object_refcounted()); +} + template object interpreter::call_managed_constructor::operator()(Ts... ts) { object ret = interp_->makeobj(new C(ts...), true, [this](C * p) { @@ -2409,6 +2689,34 @@ inline object any::as_object() { } namespace details { +template +int callback_v::checked_invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { + try { + this->invoke_impl(tcli, pv, interp, argc, argv, object_dot_method); + } catch (tcl_usage_message_printed & e) { + } catch (std::exception & e) { + for (int i = 0; i < argc; ++i) { + if (i) { std::cerr << " "; } + if (tcli->is_list(argv[i])) { + std::cerr << '{' << Tcl_GetString(argv[i]) << '}'; + } else { + std::cerr << Tcl_GetString(argv[i]); + } + } + std::cerr << ": " << e.what() << "\n"; + //Tcl_SetObjResult(interp, Tcl_NewStringObj(const_cast(e.what()), -1)); + return TCL_ERROR; + } catch (...) { + std::cerr << "Unknown error.\n"; + return TCL_ERROR; + } + if (tcli->want_abort_) { + Tcl_SetObjResult(interp, object().get_object()); + return TCL_ERROR; + } + return TCL_OK; +} + template struct conversion_offset { static const int value = @@ -2428,8 +2736,9 @@ struct conversion_offset<0, 0, arg, nargs, Conv> { template typename details::fix_variadic_return::type do_cast(interpreter * tcli, std::pair objecttypes, ppack pack, bool variadic, Tcl_Interp * interp, int objc, Tcl_Obj * CONST objv[], int getoptc, Tcl_Obj * CONST getoptv[], policies const & pol, Conv const & conv, C * this_p) { - typedef typename remove_rc::type TT; - + //typedef typename remove_rc::type TT; + typedef typename details::fix_variadic_return::type TT; + static const bool is_variadic_arg = is_variadic::value; static const bool is_optional_arg = is_optional::value; static const bool is_getopt_arg = is_getopt::value; @@ -2437,7 +2746,7 @@ typename details::fix_variadic_return::type do_cast(interpr static const bool is_old_variadic_arg = Ii + 1 == In && std::is_same::type, object const &>::value; typedef typename list_unpack::type list_unpack_t; - static const bool is_list_arg = !std::is_same::value; + static const bool is_list_arg = !std::is_same::value; static const bool cpptcl_byref = false; static const bool is_any_arg = is_any::value; @@ -2476,31 +2785,40 @@ typename details::fix_variadic_return::type do_cast(interpr if (getoptv[getopti]) { if constexpr (std::is_same >::value) { - return TT(true, true); + return TT(true, true, false); } else { - return TT(do_cast<0, ConvOffset, opt_unpack_t, In, Ii>(tcli, objecttypes, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol, conv, this_p), true); + return TT(do_cast<0, ConvOffset, opt_unpack_t, In, Ii>(tcli, objecttypes, pack, variadic, interp, getoptc, getoptv, getoptc, getoptv, pol, conv, this_p), true, false); } } else { - return TT(opt_unpack_t(), false); + return TT(opt_unpack_t(), false, false); } } else { if (objc > Ii + ArgOffset) { - return TT(do_cast(tcli, objecttypes, pack, variadic, interp, objc, objv, getoptc, getoptv, pol, conv, this_p), true); + return TT(do_cast(tcli, objecttypes, pack, variadic, interp, objc, objv, getoptc, getoptv, pol, conv, this_p), true, false); } else { - return { typename std::decay::type(), false }; + return { typename std::decay::type(), false, false }; } } } else if constexpr (is_list_arg) { - if (objv[Ii + ArgOffset]->typePtr) { - Tcl_ObjType * ot = tcli->get_objtype(); + if constexpr (is_basic_type::value || std::is_same::value) { if (tcli->is_list(objv[Ii + ArgOffset])) { - return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); + return TT(tcli, objv[Ii + ArgOffset], nullptr); } else { Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); - return TT(tcli, lo, objecttypes.first, true); + return TT(tcli, lo, nullptr, true); } } else { - return TT(nullptr, nullptr, nullptr); + if (objv[Ii + ArgOffset]->typePtr) { + Tcl_ObjType * ot = tcli->get_objtype(); + if (tcli->is_list(objv[Ii + ArgOffset])) { + return TT(tcli, objv[Ii + ArgOffset], objecttypes.first); + } else { + Tcl_Obj * lo = Tcl_NewListObj(1, objv + Ii + ArgOffset); + return TT(tcli, lo, objecttypes.first, true); + } + } else { + return TT(nullptr, nullptr, nullptr); + } } } else if constexpr (is_any_arg) { if (objv[Ii + ArgOffset]->typePtr) { @@ -2550,7 +2868,8 @@ typename details::fix_variadic_return::type do_cast(interpr if constexpr (std::is_pointer::value && std::is_class::type>::value) { return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false, *objecttypes.first); } else { - return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false); + //return tcli->template tcl_cast::type>::type>(interp, objv[Ii + ArgOffset], false); + return tcli->template tcl_cast(interp, objv[Ii + ArgOffset], false); } } } @@ -2601,6 +2920,9 @@ interpreter::function_definer & interpreter::function_definer template interpreter::class_definer & interpreter::class_definer::def2(std::string const & name, Fn f, std::index_sequence, Extra... extra) { + if (interp_->master_) { + return *this; + } const bool epol = std::tuple_size::value != sizeof...(extra); auto pol = epol ? eap(extra...) : eap(std::get(with_extra)...); @@ -2618,6 +2940,10 @@ interpreter::class_definer & interpreter::class_definer template interpreter::class_definer & interpreter::class_definer::def2(std::string const & name, overloaded const & over, std::index_sequence seq, Extra... extra) { + if (interp_->master_) { + return *this; + } + const bool epol = std::tuple_size::value != sizeof...(extra); auto convtu = std::tuple_cat(eac(extra...), eac(std::get(with_extra)...)); @@ -2631,16 +2957,23 @@ interpreter::class_definer & interpreter::class_definer interpreter::class_definer interpreter::class_(std::string const &name, init const & init_arg, Extra... extra) { - typedef callback_t, C * (*)(Ts...), true> callback_type; - - details::class_handler * ch = get_class_handler(name); - if (ch && ! init_arg.default_) { - throw tcl_error("overloaded constructors not implemented"); - } - if (!ch) { - ch = new details::class_handler(); - add_class(name, ch); - add_constructor(name, ch, new callback_type(this, &call_constructor::doit, name, extra...)); + auto * ch = static_cast *>(get_class_handler(name)); + if (master_) { + assert(ch); + if (all_callbacks_.count(name) == 0) { + add_constructor(name, ch, master_->find_callback(name)); + } + } else { + typedef callback_t, C * (*)(Ts...), true> callback_type; + + if (ch && ! init_arg.default_) { + throw tcl_error("overloaded constructors not implemented"); + } + if (!ch) { + ch = new details::class_handler(); + add_class(name, std::shared_ptr(ch), sizeof(C) <= sizeof(((Tcl_Obj *) 0)->internalRep)); + add_constructor(name, ch, std::shared_ptr(new callback_type(this, &call_constructor::doit, name, extra...))); + } } return class_definer(ch, this, false, extra...); } @@ -2648,14 +2981,16 @@ interpreter::class_definer interpreter::class_(std::string const &n template interpreter::class_definer interpreter::class_(std::string const &name, details::no_init_type const &, Extra... extra) { details::class_handler * ch = static_cast *>(get_class_handler(name)); - if (! ch) { - ch = new details::class_handler(); - add_class(name, ch); + if (master_) { + } else { + if (! ch) { + ch = new details::class_handler(); + add_class(name, ch, sizeof(C) <= sizeof(((Tcl_Obj *) 0)->internalRep)); + } } return class_definer(ch, this, false, extra...); } - inline void interpreter::setVar(std::string const & name, object const & value) { object name_obj(name); @@ -2670,6 +3005,31 @@ void interpreter::setVar(std::string const & name, T const & value) { Tcl_ObjSetVar2(interp_, name_obj.get_object(), nullptr, val_obj.get_object(), 0); } +template +void interpreter::makevalue_inplace(OT * p, object & obj) { + Tcl_Obj * o = obj.get_object(); + + if (Tcl_IsShared(o)) { + throw tcl_error("cannot modify shared object in-place"); + } + + if (sizeof(OT) <= sizeof(o->internalRep)) { + void * dp = (void *) &o->internalRep; + new (dp) OT(*p); + } else { + o->internalRep.twoPtrValue.ptr1 = new OT(*p); + } + + auto it = this->obj_type_by_cppname_.find(std::type_index(typeid(OT))); + bool ok = it != this->obj_type_by_cppname_.end(); + if (ok) { + o->typePtr = it->second + 1; + } else { + throw tcl_error("type lookup failed"); + } + Tcl_InvalidateStringRep(o); +} + template void interpreter::makeobj_inplace(OT * n, object & obj, bool owning, DEL delfn) { Tcl_Obj * o = obj.get_object(); @@ -2699,6 +3059,7 @@ void interpreter::makeobj_inplace(OT * n, object & obj, bool owning, DEL delfn) } Tcl_InvalidateStringRep(o); } + template object interpreter::makeobj(OT * p, bool owning, DELFN delfn) { object obj = object().duplicate(); @@ -2710,6 +3071,14 @@ template object interpreter::makeobj(OT * n, bool owning) { return makeobj(n, owning, (void *) 0 ); } + +template +object interpreter::makevalue(OT * v) { + object obj = object().duplicate(); + makevalue_inplace(v, obj); + return obj; +} + template void interpreter::setVar(std::string const & name, object & obj) { object name_obj(name); @@ -2739,7 +3108,12 @@ template details::result interpreter::eval(InputIterator f 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_); + if (want_abort_) { + want_abort_ = false; + throw tcl_aborted(); + } else { + throw tcl_error(interp_); + } } return details::result(interp_); diff --git a/cpptcl/cpptcl_object.h b/cpptcl/cpptcl_object.h index f252515..e40dcf0 100644 --- a/cpptcl/cpptcl_object.h +++ b/cpptcl/cpptcl_object.h @@ -79,12 +79,27 @@ class object { // constructors object(); - explicit object(bool b); object(char const *buf, size_t size); // byte array explicit object(double b); explicit object(int i); + 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(interpreter *); + explicit object(interpreter *, bool b); + object(interpreter *, char const *buf, size_t size); // byte array + explicit object(interpreter *, double b); + explicit object(interpreter *, int i); + explicit object(interpreter *, long i); + explicit object(interpreter *, char const *s); // string construction + explicit object(interpreter *, std::string const &s); // string construction + explicit object(interpreter *, Tcl_Obj *o, bool shared = false); + + // list creation // the InputIterator should give object& or Tcl_Obj* when dereferenced template object(InputIterator first, InputIterator last) : interp_(0) { @@ -93,12 +108,12 @@ class object { 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); + template object(interpreter * interp, InputIterator first, InputIterator last) : interp_(interp) { + std::vector v; + fill_vector(v, first, last); + obj_ = Tcl_NewListObj(static_cast(v.size()), v.empty() ? NULL : &v[0]); + Tcl_IncrRefCount(obj_); + } object(object const &other, bool shared = false); ~object(); @@ -139,43 +154,47 @@ class object { // (logically) non-modifying members //template T get(interpreter &i = *interpreter::defaultInterpreter) const; - template T get(interpreter &i = *interpreter::defaultInterpreter) const; + template T get() const; //interpreter &i = *interpreter::defaultInterpreter) const; char const *get() const; // string get char const *get(size_t &size) const; // byte array get - size_t size(interpreter &i = *interpreter::defaultInterpreter) const; // returns list length - object at(size_t index, interpreter &i = *interpreter::defaultInterpreter) const; - object at_ref(size_t index, interpreter &i = *interpreter::defaultInterpreter) const; + size_t size() const; //interpreter &i = *interpreter::defaultInterpreter) const; // returns list length + object at(size_t index) const; //, interpreter &i = *interpreter::defaultInterpreter) const; + object at_ref(size_t index) const; //, interpreter &i = *interpreter::defaultInterpreter) const; bool is_list() const; Tcl_Obj *get_object() const { return obj_; } - + Tcl_Obj * get_object_refcounted() { + Tcl_IncrRefCount(obj_); + return obj_; + } + // modifying members - object &append(object const &o, interpreter &i = *interpreter::defaultInterpreter); - object &append_list(object const &o, interpreter &i = *interpreter::defaultInterpreter); + object &append(object const &o); //, interpreter &i = *interpreter::defaultInterpreter); + object &append_list(object const &o); //, interpreter &i = *interpreter::defaultInterpreter); // list replace // the InputIterator should give Tcl_Obj* or object& when dereferenced - template object &replace(size_t index, size_t count, InputIterator first, InputIterator last, interpreter &i = *interpreter::defaultInterpreter) { + template object &replace(size_t index, size_t count, InputIterator first, InputIterator last) { //, interpreter &i = *interpreter::defaultInterpreter) { 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]); + int res = Tcl_ListObjReplace(interp_->get_interp(), 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()); + throw tcl_error(interp_->get_interp()); } return *this; } - object &replace(size_t index, size_t count, object const &o, interpreter &i = *interpreter::defaultInterpreter); - object &replace_list(size_t index, size_t count, object const &o, interpreter &i = *interpreter::defaultInterpreter); + object &replace(size_t index, size_t count, object const &o); //, interpreter &i = *interpreter::defaultInterpreter); + object &replace_list(size_t index, size_t count, object const &o); //, interpreter &i = *interpreter::defaultInterpreter); // helper functions for piggy-backing interpreter info - void set_interp(Tcl_Interp *interp); - Tcl_Interp *get_interp() const; + //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) { @@ -188,13 +207,13 @@ class object { const maybe_object operator()(std::string const & idx) const { Tcl_Obj *array = obj_; const char *name = Tcl_GetString(array); - Tcl_Obj *o = Tcl_GetVar2Ex(interp_, name, idx.c_str(), TCL_LEAVE_ERR_MSG); - return maybe_object(o, interp_, std::string(name), idx); + Tcl_Obj *o = Tcl_GetVar2Ex(interp_->get_interp(), name, idx.c_str(), TCL_LEAVE_ERR_MSG); + return maybe_object(o, *interp_, std::string(name), idx); } bool exists(std::string idx) const { Tcl_Obj *array = obj_; - Tcl_Obj *o = Tcl_GetVar2Ex(interp_, Tcl_GetString(array), idx.c_str(), 0); + Tcl_Obj *o = Tcl_GetVar2Ex(interp_->get_interp(), Tcl_GetString(array), idx.c_str(), 0); return (o != 0); } @@ -204,10 +223,10 @@ class object { void bind(std::string const& variableName) { object n(variableName); Tcl_IncrRefCount(obj_); - if (interp_ == nullptr) { - interp_ = interpreter::getDefault()->get(); - } - Tcl_ObjSetVar2(interp_, n.get_object(), nullptr, obj_, 0); + //if (interp_ == nullptr) { + // interp_ = interpreter::getDefault()->get(); + //} + Tcl_ObjSetVar2(interp_->get_interp(), n.get_object(), nullptr, obj_, 0); } // Bind array value in TCL interpreter @@ -217,10 +236,10 @@ class object { object n(variableName); object i(indexName); Tcl_IncrRefCount(obj_); - if (interp_ == nullptr) { - interp_ = interpreter::getDefault()->get(); - } - Tcl_ObjSetVar2(interp_, n.get_object(), i.get_object(), obj_, 0); + //if (interp_ == nullptr) { + // interp_ = interpreter::getDefault()->get(); + //} + Tcl_ObjSetVar2(interp_->get_interp(), n.get_object(), i.get_object(), obj_, 0); } std::string asString() const; @@ -233,7 +252,7 @@ class object { // helper function used from copy constructors void init(Tcl_Obj *o, bool shared); - static Tcl_Obj * default_object_; + static thread_local Tcl_Obj * default_object_; static void static_initialize() { default_object_ = Tcl_NewObj(); Tcl_IncrRefCount(default_object_); @@ -264,7 +283,8 @@ class object { return Tcl_IsShared(obj_); } Tcl_Obj *obj_; - Tcl_Interp *interp_; + interpreter * interp_; + //Tcl_Interp *interp_; }; #endif /* CPPTCL_OBJECT_H */ From 63d289f82c3d4ec96e671b294d36ae0651e7475c Mon Sep 17 00:00:00 2001 From: bunkerbunk Date: Sat, 11 Dec 2021 21:28:07 +0100 Subject: [PATCH 10/10] tcl threads compatibility, allow options to be disabled easily --- cpptcl.cc | 83 +++++-------------------------- cpptcl/cpptcl.h | 126 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 118 insertions(+), 91 deletions(-) diff --git a/cpptcl.cc b/cpptcl.cc index ef3caab..d399850 100644 --- a/cpptcl.cc +++ b/cpptcl.cc @@ -87,7 +87,6 @@ void details::set_result(Tcl_Interp *interp, named_pointer_result p) { Tcl_SetObjResult(interp, to); } - void details::set_result(Tcl_Interp *interp, object const &o) { Tcl_SetObjResult(interp, o.get_object()); } void details::check_params_no(int objc, int required, int maximum, const std::string &message) { @@ -121,43 +120,6 @@ object details::get_var_params(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv namespace // anonymous { -// map of polymorphic callbacks -typedef map> callback_interp_map; -typedef map callback_map; - -callback_map callbacks; -callback_map constructors; - -// map of call policies -//typedef map policies_interp_map; -//typedef map policies_map; - -//policies_map call_policies; - - //typedef std::map all_definitions2_t; - //typedef std::map all_definitions_t; - //all_definitions_t all_definitions; - -// map of object handlers -typedef map class_handlers_map; - //typedef map class_handlers_map; - -class_handlers_map class_handlers; - -#if 0 - // helper for finding call policies - returns true when found -bool find_policies(Tcl_Interp *interp, string const &cmdName, policies_interp_map::iterator &piti) { - policies_map::iterator pit = call_policies.find(interp); - - if (pit == call_policies.end()) { - return false; - } - - piti = pit->second.find(cmdName); - return piti != pit->second.end(); -} -#endif - extern "C" int object_handler(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); // actual functions handling various callbacks @@ -480,17 +442,6 @@ void class_handler_base::register_method(string const & name, callback_base * oc //policies_[name] = p; } -#if 0 -policies &class_handler_base::get_policies(string const &name) { - policies_map_type::iterator it = policies_.find(name); - if (it == policies_.end()) { - throw tcl_error("Trying to use non-existent policy: " + name); - } - - return it->second; -} -#endif - thread_local Tcl_Obj * object::default_object_ = nullptr; object::object() : interp_(nullptr) { @@ -849,6 +800,8 @@ int interpreter::dot_call_handler(ClientData cd, Tcl_Interp * interp, int objc, } interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { + static_initialize(); + interp_ = Tcl_CreateInterp(); owner_ = true; if (defaultInterpreter) { @@ -859,6 +812,8 @@ interpreter::interpreter() : tin_(nullptr), tout_(nullptr), terr_(nullptr) { } interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_(nullptr), terr_(nullptr) { + static_initialize(); + interp_ = interp; if (! interp_) { interp_ = Tcl_CreateInterp(); } @@ -878,6 +833,7 @@ interpreter::interpreter(Tcl_Interp *interp, bool owner) : tin_(nullptr), tout_( interpreter::interpreter(const interpreter &i) : interp_(i.interp_), owner_(i.owner_), tin_(nullptr), tout_(nullptr), terr_(nullptr), list_type_(i.list_type_), cmdname_type_(i.cmdname_type_), object_namespace_(i.object_namespace_) { + static_initialize(); } void interpreter::custom_construct(const char * c_name, const char * o_name, void * p) { @@ -902,9 +858,9 @@ void interpreter::find_standard_types() { } interpreter::~interpreter() { + clear_definitions(); if (owner_) { // clear all callback info belonging to this interpreter - clear_definitions(interp_); Tcl_DeleteInterp(interp_); } } @@ -1003,33 +959,18 @@ void interpreter::create_alias(string const &cmd, interpreter &targetInterp, str } } -void interpreter::clear_definitions(Tcl_Interp *interp) { +void interpreter::clear_definitions() { for (auto & c : all_callbacks_) { Tcl_DeleteCommand(get_interp(), c.first.c_str()); } - -#if 0 - { - auto it = class_handlers.find(interp); - if (it != class_handlers.end()) { - for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { - delete it2->second; - } - } - class_handlers.erase(interp); - } -#endif } void interpreter::add_function(string const &name, std::shared_ptr cb) { - //Tcl_CreateObjCommand(interp_, name.c_str(), callback_handler, cb, 0); cb->install(this); all_callbacks_.insert(std::pair(name, cb)); - //all_definitions[interp_][name] = cb; } void interpreter::add_class(string const &name, std::shared_ptr chb, bool is_inline) { - //class_handlers[name] = chb; all_classes_.insert(std::pair(name, chb)); Tcl_ObjType * ot = get_objtype(name.c_str()); if (ot) { @@ -1042,7 +983,6 @@ details::class_handler_base * interpreter::get_class_handler(std::string const & return it2->second.get(); } - void interpreter::add_constructor(string const &name, class_handler_base * chb, std::shared_ptr cb) { constructor_handler_client_data_t * up = new constructor_handler_client_data_t; up->cb = cb.get(); @@ -1051,7 +991,6 @@ void interpreter::add_constructor(string const &name, class_handler_base * chb, Tcl_CreateObjCommand(interp_, name.c_str(), constructor_handler, up, 0); all_callbacks_.insert(std::pair(name, cb)); - // all_definitions[interp_][name] = cb; } void interpreter::add_managed_constructor(string const &name, class_handler_base * chb, std::shared_ptr cb) { @@ -1062,7 +1001,6 @@ void interpreter::add_managed_constructor(string const &name, class_handler_base Tcl_CreateObjCommand(interp_, name.c_str(), managed_constructor_handler, up, 0); all_callbacks_.insert(std::pair(name, cb)); - //all_definitions[interp_][name] = cb; } int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { @@ -1080,7 +1018,6 @@ unsigned int tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool return res; } - long tcl_cast::from(Tcl_Interp *interp, Tcl_Obj *obj, bool) { long res; if (Tcl_GetLongFromObj(interp, obj, &res) != TCL_OK) { @@ -1189,7 +1126,7 @@ void interpreter::post_process_policies(Tcl_Interp *interp, policies &pol, Tcl_O } tcl_error::tcl_error(std::string const & msg) : msg_(msg), std::runtime_error(msg) { - backtrace_size_ = backtrace(backtrace_, sizeof(backtrace_) / sizeof(backtrace_[0])); + //backtrace_size_ = backtrace(backtrace_, sizeof(backtrace_) / sizeof(backtrace_[0])); } tcl_error::tcl_error(Tcl_Interp * interp) : std::runtime_error(Tcl_GetString(Tcl_GetObjResult(interp))), msg_(Tcl_GetString(Tcl_GetObjResult(interp))) {} const char * tcl_error::what() const throw() { @@ -1222,5 +1159,7 @@ const char * tcl_error::what() const throw() { decltype(interpreter::obj_type_by_tclname_) interpreter::obj_type_by_tclname_; decltype(interpreter::obj_type_by_cppname_) interpreter::obj_type_by_cppname_; - + decltype(interpreter::capture_callback) interpreter::capture_callback; + bool interpreter::static_initialized_ = false; } + diff --git a/cpptcl/cpptcl.h b/cpptcl/cpptcl.h index c769f00..4b8962c 100644 --- a/cpptcl/cpptcl.h +++ b/cpptcl/cpptcl.h @@ -317,6 +317,11 @@ struct getopt : public optional { using optional::operator std::optional; }; +template +struct getopt_d : public optional { + using optional::optional; +}; + template using opt = optional; @@ -632,6 +637,20 @@ struct is_getopt const &> { static const bool value = true; }; +template +struct is_getopt_d { + static const bool value = false; +}; +template +struct is_getopt_d > { + static const bool value = true; +}; +template +struct is_getopt_d const &> { + static const bool value = true; +}; + + template struct optional_unpack { typedef T type; @@ -644,6 +663,10 @@ template struct optional_unpack > { typedef T type; }; +template +struct optional_unpack > { + typedef T type; +}; template struct list_unpack { @@ -675,6 +698,15 @@ template struct num_getopt { static const int value = num_getopt::value + (is_getopt::value ? 1 : 0); }; +template +struct num_getopt_d { + static const int value = 0; +}; +template +struct num_getopt_d { + static const int value = num_getopt_d::value + (is_getopt_d::value ? 1 : 0); +}; + template struct getopt_index { @@ -729,12 +761,20 @@ struct visit_impl { if (arg_list_t li = a.template as()) { if constexpr (std::is_invocable::value) { for (auto && i : li) { - f(i); + if constexpr (std::is_same::type, bool>::value) { + if (! f(i)) break; + } else { + f(i); + } } } else if constexpr (std::is_invocable::value) { for (std::size_t i = 0; i < li.size(); ++i) { auto both = li.with_obj_at(i); - f(both.first, both.second); + if constexpr (std::is_same::type, bool>::value) { + if (! f(both.first, both.second)) break; + } else { + f(both.first, both.second); + } } } return true; @@ -778,7 +818,14 @@ struct generate_hasarg { template struct generate_hasarg { static void invoke(bool * arr, std::string * opts, std::string * defaults, const char * p, bool may_have_defaults) { - if (!is_getopt::value) { + if constexpr (is_getopt_d::value) { + while (*p && isspace(*p)) ++p; + if (p) { + const char * pend = p; + while (*pend && !isspace(*pend) && *pend != '=') ++pend; + } + generate_hasarg::invoke(arr, opts, defaults, p, may_have_defaults); + } else if constexpr (!is_getopt::value) { generate_hasarg::invoke(arr, opts, defaults, p, may_have_defaults); } else { arr[I] = !std::is_same::type, getopt >::value; @@ -918,6 +965,11 @@ template struct fix_variadic_return const &, Last> { typedef getopt type; }; +template +struct fix_variadic_return const &, Last> { + typedef getopt_d type; +}; + template struct fix_variadic_return const &, Last> { typedef list type; @@ -1127,7 +1179,7 @@ class callback_v : public callback_base_type::type { policies policies_; functor_type f_; - static const int num_opt = num_getopt::value; + static const int num_opt = num_getopt::value + num_getopt_d::value; std::string opts_[num_opt], defaults_[num_opt]; bool has_arg_[num_opt + 1] = { false }; @@ -1537,10 +1589,31 @@ class interpreter { void want_abort() { want_abort_ = true; } - private: + + static std::function capture_callback; +private: interpreter(const interpreter &i); interpreter(); + static bool static_initialized_; + + static int capture_interpreter_init(Tcl_Interp * interp) { + if (capture_callback) { + interpreter * tcli = new interpreter(interp, false); + capture_callback(tcli); + } + return TCL_OK; + } + + static void static_initialize() { + if (! static_initialized_) { + Tcl_StaticPackage(nullptr, "Capture_interpreter", capture_interpreter_init, capture_interpreter_init); + static_initialized_ = true; + } + } + + + class TclChannelStreambuf : public std::streambuf { private: Tcl_Channel channel_; @@ -1839,6 +1912,10 @@ class interpreter { template function_definer & defvar(std::string const & name, T C::*t) COLD; + //template + //function_definer & defvar(std::string const & name, T C::&t) { + // return defvar(name, &t); + //} C * this_p_; interpreter * interp_; @@ -1911,6 +1988,7 @@ class interpreter { void def(std::string const & name, R (C::*f)(Ts...), C * this_p, Extra... extra) { def(name, [this_p, f](Ts... args) { return (this_p->*f)(args...); }, extra...); } + template void def(std::string const & name, overloaded over, Extra... extra) { @@ -2108,7 +2186,8 @@ class interpreter { o->bytes[name.size()] = 0; o->length = name.size(); } - static int set(Tcl_Interp *, Tcl_Obj *) { + static int set(Tcl_Interp * interp, Tcl_Obj * o) { + //std::cerr << "setfromany " << interp << " " << o << "\n"; return TCL_OK; } static int set_v(Tcl_Interp *, Tcl_Obj *) { @@ -2256,7 +2335,11 @@ class interpreter { if (Tcl_ListObjIndex(i, o, 0, &o2) == TCL_OK) { if (otp == o2->typePtr) { return (T) o2->internalRep.twoPtrValue.ptr1; + } else { + throw tcl_error("function argument has wrong type. expect " + details::tcl_typename(otp) + ", got " + details::tcl_typename(o2->typePtr)); } + } else { + throw tcl_error("internal error. cannot access valid list index"); } } else { throw tcl_error("Expected single object, got list"); @@ -2338,7 +2421,7 @@ class interpreter { void create_namespace(std::string const &name); // helper for cleaning up callbacks in non-managed interpreters - void clear_definitions(Tcl_Interp *); + void clear_definitions(); private: void operator=(const interpreter &); @@ -2501,8 +2584,9 @@ void callback_v::uninstall(interpreter * tcli) { template void callback_v::invoke_impl(interpreter * tcli, void * pv, Tcl_Interp * interp, int argc, Tcl_Obj * const argv [], bool object_dot_method) { - static const int num_gopt = num_getopt::value; - static const int num_opt = num_optional::value; + static const int num_gopt = num_getopt::value; + static const int num_gopt_d = num_getopt_d::value; + static const int num_opt = num_optional::value; Tcl_Obj * getopt_argv[num_gopt + 1] = { nullptr }; bool getopt_allocated[num_gopt + 1] = { false }; Tcl_Obj boolean_dummy_object; @@ -2538,7 +2622,7 @@ void callback_v::invoke_impl(interpreter * tcli, voi return; } - if (num_gopt) { + if constexpr (num_gopt) { for (; ai < argc; ++ai) { char * s = Tcl_GetString(argv[ai]); if (s[0] == '-') { @@ -2580,7 +2664,7 @@ void callback_v::invoke_impl(interpreter * tcli, voi } } } - check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ || policies_.variadic_ ? 1 : 0) - num_gopt - num_opt, has_variadic_ || policies_.variadic_ ? -1 : sizeof...(Ts) - num_gopt, policies_.usage_); + check_params_no(argc - ai, sizeof...(Ts) - (has_variadic_ || policies_.variadic_ ? 1 : 0) - num_gopt - num_opt - num_gopt_d, has_variadic_ || policies_.variadic_ ? -1 : sizeof...(Ts) - num_gopt - num_gopt_d, policies_.usage_); auto dealloc = [&](void *) { if (may_have_defaults && any_getopt_allocated) { @@ -2593,7 +2677,7 @@ void callback_v::invoke_impl(interpreter * tcli, voi }; std::unique_ptr dealloc_raii(0, dealloc); - do_invoke(std::make_index_sequence(), tcli, (C *) pv, interp, argc - ai, argv + ai, num_gopt, getopt_argv, policies_, void_return::value>()); + do_invoke(std::make_index_sequence(), tcli, (C *) pv, interp, argc - ai, argv + ai, num_gopt + num_gopt_d, getopt_argv, policies_, void_return::value>()); if (pol_t::postproc) { tcli->post_process_policies(interp, policies_, argv + ai, false); } @@ -2753,9 +2837,13 @@ typename details::fix_variadic_return::type do_cast(interpr static const bool is_any_list_arg = is_any_arg && all_lists::value; typedef typename optional_unpack::type opt_unpack_t; - + static const int conversion = conversion_offset<0, std::tuple_size::value, Ii + ConvOffset, In + ConvOffset, Conv>::value; + if constexpr (is_getopt_d::value) { + return TT(opt_unpack_t(), false, false); + } + if constexpr (conversion >= 0) { using conv_t = typename std::tuple_element::type; if constexpr (std::is_invocable::value) { @@ -2889,12 +2977,12 @@ void interpreter::defvar(std::string const & name, T & v) { template template interpreter::function_definer & interpreter::function_definer::defvar(std::string const & name, T C::*t) { - return def(name, [t] (opt const & arg) -> T { - if (arg) { - *t = arg; - } - return *t; - }); + return this->def(name, [t, this] (opt const & arg) -> T { + if (arg) { + this_p_->*t = arg; + } + return this_p_->*t; + }); } template