diff libinterp/octave-value/ov-classdef.cc @ 18263:b5be1a2aa5ab

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.
author Michael Goffioul <michael.goffioul@gmail.com>
date Sun, 12 Jan 2014 15:54:44 -0500
parents 69990d5edcc2
children 81c1edd70bfd
line wrap: on
line diff
--- 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<std::string> external_methods =
+            load_path::methods (full_class_name);
+
+          for (std::list<std::string>::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);