From octave-maintainers-request at bevo dot che dot wisc dot edu Thu Jan 15 18:17:33 2004 Subject: octave.object(support) (Modified by Paul Kienzle) From: Paul Kienzle To: pkienzle at comcast dot net Date: Thu, 15 Jan 2004 15:02:42 -0500 --Apple-Mail-14--731733745 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII; format=flowed Hello, I've been playing with a new type that allows you to define objects and methods implemented in oct-files, but callable directly from scripts using the usual object.method(args) syntax. It works reasonably well without any change to octave sources. The following operations work: # Can create a new type, and query its properties x = vtkitem(1) [a,b] = x.query(1,2) # The object inside is persistent x.set(2); x # It can be assigned t = vtkitem(3) u = t s.x = t c = { t } # The destructor is called in the appropriate places clear x # when cleared vtkitem(4) # not when it is ans clear ans vtkitem(5).set(6) # but when it is an intermediate function f, t = vtkitem(4), end f # when a function returns clear x u t # not cleared: still a copy in c{1} c{1} = 1; # cleared: last copy is gone c = { vtkitem(7) }; clear c # goes away with the cell array s.x = vtkitem(8); clear s # cleared when struct is destroyed s.x = vtkitem(9); s.x = 1; # and when field is replaced # Hmmm... 'clear s.x c{2:4} d[2,:]' is nice syntax. # Can't do it though without making clear a keyword. # You can see the available methods x = vtkitem(9); methods(x) There are still some issues though. The following operations do not work: # The following does not call the subsasgn method: x = vtkitem(10); x.query = 1; # The following do not call subsref with nargout=2: c = { vtkitem(11) } [a,b] = c{1}.query [a,b] = vtkitem(12).query c.x = vtkitem(13) [a,b] = c.x.query # The following do not call the destructor for vtkitem function f, static t=vtkitem(14); end f clear f # persistent variables not deleted? x = vtkitem(15); exit # toplevel symbol table not deleted? global x = vtkitem(16); exit # global symbol table not deleted? There is no support for operator overloading, and since the interface code for multiple different classes will share the same class id, my dispatch hack won't allow function overloading either. The code is attached. Paul Kienzle pkienzle at users dot sf dot net --Apple-Mail-14--731733745 Content-Transfer-Encoding: 7bit Content-Type: application/octet-stream; x-unix-mode=0666; name="vtkitem.cc" Content-Disposition: attachment; filename=vtkitem.cc /* Copyright (C) 2001 John W. Eaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Octave; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* The following is a template for creating a new class: =============================================================== class item_class : public object_base { ... data members ... public: item_class(...constructor args...) { ... } ~item_class() { ... } void print(std::ostream& out, bool pickle) { ... } METHOD_DECLARE(method1); METHOD_DECLARE(method2); ... } METHOD_DEFINE(item_class,method1,args,nargout) { ... } METHOD_DEFINE(item_class,method2,args,nargout) { ... } ... DEFUN_DLD(item,args,nargout) { ... check args and convert into constructor args ... // Create the item item_class* object = new item_class(...constructor args...); // Set up the object method dispatch table static method_table dispatch; if (dispatch.size() == 0) { METHOD_DISPATCH(dispatch,item,method1); METHOD_DISPATCH(dispatch,item,method2); ... } // Return the object. ret(0) = new octave_object(object,dispatch); return ret; } ============================================================== */ // The following must be defined for recent versions of Octave. #define TYPEID_HAS_CLASS #include #include #include #include #include #include #include #ifdef HAVE_SLLIST_H #define LIST SLList #define LISTSIZE length #define SUBSREF_STRREF #else #include #define LIST std::list #define LISTSIZE size #define SUBSREF_STRREF & #endif class object_base { public: object_base() {} virtual ~object_base() {} virtual void print(std::ostream&, bool=false) {} } ; typedef octave_value_list method_function(object_base*, const octave_value_list&, int); typedef std::map method_table; class octave_object : public octave_base_value { object_base* object; const method_table* dispatch; method_function* dotref(const octave_value_list&); public: // make a new octave value from an object. octave_object (object_base* o, const method_table& d) : object(o), dispatch(&d) {} // XXX FIXME XXX if we get deleted, we should clean up the // underlying object, but I'm not sure if there are any // extra copies kicking around. ~octave_object (void) { std::cout << "Calling object destructor for "; object->print(std::cout); delete object; } bool is_defined (void) const { return true; } // since we are a function, we won't see do_index_op octave_value do_index_op (const octave_value_list &, int) { error("octave_object: do_index_op(idx,can_resize)"); return octave_value(); } // x.v = y // x(idx).v = y // x{idx}.v = y octave_value subsasgn (const std::string& type, const LIST& idx, const octave_value& rhs) { error("octave_object: subsasgn(type,idx,rhs)"); return octave_value (); } // x.v // x(idx).v // x{idx}.v octave_value subsref (const std::string SUBSREF_STRREF type, const LIST& idx) { octave_stdout << "octave_object: subsref(type,idx)" << std::endl; return subsref (type, idx, 1)(0); } // [i,j] = x(i) octave_value_list do_multi_index_op (int, const octave_value_list&) { error("octave_object: do_multi_index_op(nargout,args)"); return octave_value_list(); } // [i,j] = x.v(...) octave_value_list subsref (const std::string SUBSREF_STRREF type, const LIST& idx, int nargout); void print (std::ostream& os, bool pr_as_read=false) const { object->print(os, pr_as_read); } void methods (std::ostream& os) const { for (method_table::const_iterator p=dispatch->begin(); p != dispatch->end(); p++) { os << p->first << std::endl; } } private: octave_object (void) {} octave_object (const octave_object& m); DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA DECLARE_OCTAVE_ALLOCATOR }; DEFINE_OCTAVE_ALLOCATOR (octave_object); #ifdef TYPEID_HAS_CLASS DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_object, "object","object") #else DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_object, "object") #endif method_function* octave_object::dotref (const octave_value_list& idx) { assert (idx.length () == 1); std::string nm = idx(0).string_value (); method_table::const_iterator p = dispatch->find (nm); if (p != dispatch->end ()) return p->second; error ("object has no member `%s'", nm.c_str ()); return NULL; } static bool any_arg_is_magic_colon (const octave_value_list& args) { int nargin = args.length (); for (int i = 0; i < nargin; i++) if (args(i).is_magic_colon ()) return true; return false; } octave_value_list octave_object::subsref (const std::string SUBSREF_STRREF type, const LIST& idx, int nargout) { octave_value_list retval; size_t skip = 1; switch (type[0]) { case '.': { method_function *m = dotref (idx.front ()); if (m == NULL) return retval; if (idx.LISTSIZE () < 2 || type[1] != '(') { retval = (*m)(object,octave_value_list(),nargout); } else { skip = 2; LIST::const_iterator pidx = idx.begin(); pidx++; if (any_arg_is_magic_colon (*pidx)) { error ("invalid use of colon in method argument list"); } else { retval = (*m)(object,*pidx,nargout); } } } break; case '(': case '{': { std::string nm = type_name (); error ("%s cannot be indexed with %c", nm.c_str (), type[0]); } break; default: panic_impossible (); } if (!error_state && idx.LISTSIZE () > skip) retval = retval(0).next_subsref (type, idx, skip); return retval; } inline bool is_object(const octave_value &v) { return v.type_id () == octave_object::static_type_id (); } inline const octave_object& as_object(const octave_value &v) { return (const octave_object&) v.get_rep(); } DEFUN_DLD(methods, args, nargout, "\ Display a list of methods available for a given class.") { octave_value_list retval; size_t nargin = args.length(); if (nargin != 1) { print_usage("methods"); return retval; } if (is_object(args(0))) as_object(args(0)).methods(octave_stdout); // XXX FIXME XXX return a cell array of method names // if nargout is 1. return retval; } #define METHOD_DECLARE(M) \ octave_value_list M(const octave_value_list&, int) #define METHOD_DEFINE(C,M,args,nargout) \ static octave_value_list C ## __ ## M (object_base *object__, \ const octave_value_list& a, int n) \ { return ((C *)object__)->M(a,n); } \ octave_value_list \ C::M(const octave_value_list& args, int nargout) #define METHOD_DISPATCH(table,C,M) \ table[#M] = &C ## __ ## M // ======================================================================= // Example class which stores an octave value class item_class : public object_base { octave_value data; public: item_class(const octave_value& v): data(v) {} ~item_class() { octave_stdout << "destructor "; print(octave_stdout,false); } void print(std::ostream& os, bool pickle) { if (pickle) { os << "vtkitem("; data.print(os,pickle); os << ")"; } else { os << "vtkitem "; data.print(os,pickle); os << std::endl; } } METHOD_DECLARE(query); METHOD_DECLARE(set); } ; // query method prints its arguments and returns the stored octave value // plus a second value just to test the full [v,s]=t.query(n) functionality. METHOD_DEFINE(item_class,query,args,nargout) { octave_value_list ret; int nargin = args.length(); octave_stdout << "item.query called with " << std::endl; for (int i=0; i < nargin; i++) { octave_stdout << i << ": "; args(i).print(octave_stdout); } octave_stdout << "nargout is " << nargout << std::endl; octave_stdout << "value is "; data.print(octave_stdout); octave_stdout << std::endl; ret(1) = octave_value("ret2"); ret(0) = data; return ret; } // set method modifies the stored value with a new one. METHOD_DEFINE(item_class,set,args,nargout) { octave_value_list ret; int nargin = args.length(); if (nargin > 1) { error("item.set called with too many arguments."); return ret; } if (nargin) data = args(0); return data; } // Object constructor DEFUN_DLD(vtkitem, args, , "\ a = vtkitem(v)\n\ \n\ Print the value v in response to a.query(1)\n\ ") { octave_value_list ret; int nargin = args.length(); if (nargin < 1 || nargin > 2) { print_usage("vtkitem"); return ret; } item_class *object = new item_class(args(0)); // Set up the object method dispatch table static method_table dispatch; if (dispatch.size() == 0) { METHOD_DISPATCH(dispatch,item_class,query); METHOD_DISPATCH(dispatch,item_class,set); } // Return the object. ret(0) = new octave_object(object,dispatch); return ret; } --Apple-Mail-14--731733745--