# HG changeset patch # User Michael Goffioul # Date 1389560084 18000 # Node ID b5be1a2aa5ab6a8ffaf92597cec4ed286226c1c9 # Parent 69990d5edcc2143e1844edcf95fe1fdd864d7478 Initial implementation for classdef methods in separate files. * libinterp/octave-value/ov-classdef.h (cdef_class::make_meta_class): New argument to tell whether the class is loaded from a @-folder. (cdef_method::cdef_method_rep::dispatch_type): New member used for external methods. (cdef_method::cdef_method_rep::cdef_method_rep): Initialize it. (cdef_method::cdef_method_rep::is_external, cdef_method::cdef_method_rep::mark_as_external): New methods, use it. (cdef_method::is_external, cdef_method::mark_as_external): Likewise. * libinterp/octave-value/ov-classdef.cc (is_dummy_method): New static method. (make_method): Use it to initialize "external" state of created method. (cdef_class::make_meta_class): New argument to know whether we're loading from a @-folder. Scan existing methods from load-path and create corresponding method entries in the class definition. (cdef_method::cdef_method_rep::check_method): Load external method if required. (cdef_method::cdef_method_rep::execute): Check error_state after calling check_method. * libinterp/parse-tree/pt-classdef.h (tree_classdef::make_meta_class): New argument to tell whether the class is loaded from a @-folder. * libinterp/parse-tree/pt-classdef.cc (tree_classdef::make_meta_class): Likewise. Pass new argument to cdef_class::make_meta_class. * libinterp/parse-tree/oct-parse.in.yy (parse_fcn_file): Pass new is_at_folder argument. diff -r 69990d5edcc2 -r b5be1a2aa5ab libinterp/octave-value/ov-classdef.cc --- a/libinterp/octave-value/ov-classdef.cc Sun Jan 12 15:54:43 2014 -0500 +++ b/libinterp/octave-value/ov-classdef.cc Sun Jan 12 15:54:44 2014 -0500 @@ -32,6 +32,7 @@ #include "ov-classdef.h" #include "ov-fcn-handle.h" #include "ov-typeinfo.h" +#include "ov-usr-fcn.h" #include "pt-assign.h" #include "pt-classdef.h" #include "pt-funcall.h" @@ -360,6 +361,27 @@ return false; } +static bool +is_dummy_method (const octave_value& fcn) +{ + bool retval = false; + + if (fcn.is_defined ()) + { + if (fcn.is_user_function ()) + { + octave_user_function *uf = fcn.user_function_value (true); + + if (! uf || ! uf->body ()) + retval = true; + } + } + else + retval = true; + + return retval; +} + bool is_method_executing (const octave_value& ov, const cdef_object& obj) { @@ -763,6 +785,9 @@ meth.set_function (fcn); + if (is_dummy_method (fcn)) + meth.mark_as_external (cls.get_name ()); + return meth; } @@ -2455,7 +2480,7 @@ } cdef_class -cdef_class::make_meta_class (tree_classdef* t) +cdef_class::make_meta_class (tree_classdef* t, bool is_at_folder) { cdef_class retval; std::string class_name, full_class_name; @@ -2598,6 +2623,43 @@ } } + if (is_at_folder) + { + // Look for all external methods visible on octave path at the + // time of loading of the class. + // + // TODO: This is an "extension" to Matlab behavior, which only + // looks in the @-folder containing the original classdef + // file. However, this is easier to implement it that way at + // the moment. + + std::list external_methods = + load_path::methods (full_class_name); + + for (std::list::const_iterator it = external_methods.begin (); + it != external_methods.end (); ++it) + { + // TODO: should we issue a warning if the method is already + // defined in the classdef file? + + if (*it != class_name + && ! retval.find_method (*it, true).ok ()) + { + // Create a dummy method that is used until the actual + // method is loaded. + + octave_user_function *fcn = new octave_user_function (); + + fcn->stash_function_name (*it); + + cdef_method meth = make_method (retval, *it, + octave_value (fcn)); + + retval.install_method (meth); + } + } + } + // Property blocks // FIXME: default property expression should be able to call static @@ -2847,7 +2909,49 @@ void cdef_method::cdef_method_rep::check_method (void) { - // FIXME: check whether re-load is needed + if (is_external ()) + { + if (is_dummy_method (function)) + { + std::string name = get_name (); + std::string cls_name = dispatch_type; + std::string pack_name; + + size_t pos = cls_name.rfind ('.'); + + if (pos != std::string::npos) + { + pack_name = cls_name.substr (0, pos); + cls_name = cls_name.substr (pos + 1); + } + + std::string dir_name; + std::string file_name = load_path::find_method (cls_name, name, + dir_name, pack_name); + + if (! file_name.empty ()) + { + octave_function *fcn = load_fcn_from_file (file_name, dir_name, + dispatch_type, + pack_name); + + if (fcn) + { + function = octave_value (fcn); + + make_function_of_class (dispatch_type, function); + } + } + } + else + { + // FIXME: check out-of-date status + } + + if (is_dummy_method (function)) + ::error ("no definition found for method `%s' of class `%s'", + get_name ().c_str (), dispatch_type.c_str ()); + } } octave_value_list @@ -2868,7 +2972,7 @@ { check_method (); - if (function.is_defined ()) + if (! error_state && function.is_defined ()) { retval = execute_ov (function, args, nargout); } @@ -2899,10 +3003,10 @@ { check_method (); - octave_value_list new_args; - - if (function.is_defined ()) + if (! error_state && function.is_defined ()) { + octave_value_list new_args; + new_args.resize (args.length () + 1); new_args(0) = to_ov (obj); diff -r 69990d5edcc2 -r b5be1a2aa5ab libinterp/octave-value/ov-classdef.h --- a/libinterp/octave-value/ov-classdef.h Sun Jan 12 15:54:43 2014 -0500 +++ b/libinterp/octave-value/ov-classdef.h Sun Jan 12 15:54:44 2014 -0500 @@ -827,7 +827,8 @@ void delete_object (cdef_object obj) { get_rep ()->delete_object (obj); } - static cdef_class make_meta_class (tree_classdef* t); + static cdef_class make_meta_class (tree_classdef* t, + bool is_at_folder = false); octave_function* get_method_function (const std::string& nm); @@ -1019,7 +1020,9 @@ cdef_method_rep : public cdef_meta_object_rep { public: - cdef_method_rep (void) : cdef_meta_object_rep () { } + cdef_method_rep (void) + : cdef_meta_object_rep (), function (), dispatch_type () + { } cdef_object_rep* copy (void) const { return new cdef_method_rep(*this); } @@ -1037,6 +1040,11 @@ bool check_access (void) const; + bool is_external (void) const { return ! dispatch_type.empty (); } + + void mark_as_external (const std::string& dtype) + { dispatch_type = dtype; } + octave_value_list execute (const octave_value_list& args, int nargout, bool do_check_access = true, const std::string& who = std::string ()); @@ -1057,7 +1065,9 @@ private: cdef_method_rep (const cdef_method_rep& m) - : cdef_meta_object_rep (m), function (m.function) { } + : cdef_meta_object_rep (m), function (m.function), + dispatch_type (m.dispatch_type) + { } void check_method (void); @@ -1069,6 +1079,10 @@ private: octave_value function; + + // When non-empty, the method is externally defined and this member + // is used to cache the dispatch type to look for the method. + std::string dispatch_type; }; public: @@ -1125,6 +1139,11 @@ bool is_constructor (void) const { return get_rep ()->is_constructor (); } + bool is_external (void) const { return get_rep ()->is_external (); } + + void mark_as_external (const std::string& dtype) + { get_rep ()->mark_as_external (dtype); } + private: cdef_method_rep* get_rep (void) { return dynamic_cast (cdef_object::get_rep ()); } diff -r 69990d5edcc2 -r b5be1a2aa5ab libinterp/parse-tree/oct-parse.in.yy --- a/libinterp/parse-tree/oct-parse.in.yy Sun Jan 12 15:54:43 2014 -0500 +++ b/libinterp/parse-tree/oct-parse.in.yy Sun Jan 12 15:54:44 2014 -0500 @@ -3841,7 +3841,10 @@ if (fcn_ptr) panic_impossible (); - fcn_ptr = parser.classdef_object->make_meta_class (); + bool is_at_folder = ! dispatch_type.empty (); + + fcn_ptr = + parser.classdef_object->make_meta_class (is_at_folder); } else if (fcn_ptr) { diff -r 69990d5edcc2 -r b5be1a2aa5ab libinterp/parse-tree/pt-classdef.cc --- a/libinterp/parse-tree/pt-classdef.cc Sun Jan 12 15:54:43 2014 -0500 +++ b/libinterp/parse-tree/pt-classdef.cc Sun Jan 12 15:54:44 2014 -0500 @@ -233,10 +233,10 @@ // Classdef octave_function* -tree_classdef::make_meta_class (void) +tree_classdef::make_meta_class (bool is_at_folder) { octave_value retval; - cdef_class cls = cdef_class::make_meta_class (this); + cdef_class cls = cdef_class::make_meta_class (this, is_at_folder); if (cls.ok ()) return cls.get_constructor_function (); diff -r 69990d5edcc2 -r b5be1a2aa5ab libinterp/parse-tree/pt-classdef.h --- a/libinterp/parse-tree/pt-classdef.h Sun Jan 12 15:54:43 2014 -0500 +++ b/libinterp/parse-tree/pt-classdef.h Sun Jan 12 15:54:44 2014 -0500 @@ -628,7 +628,7 @@ const std::string& package_name (void) const { return pack_name; } - octave_function* make_meta_class (void); + octave_function* make_meta_class (bool is_at_folder = false); tree_classdef *dup (symbol_table::scope_id scope, symbol_table::context_id context) const;