changeset 16048:10142aad4b9f classdef

Implement indirect method call: fun(obj, ...). * libinterp/octave-value/ov-classdef.h (class cdef_manager): New class. (cdef_method::cdef_method_rep::meta_subsref, cdef_method::cdef_method_rep::meta_is_postfix_index_handled): New methods. * libinterp/octave-value/ov-classdef.cc (all_packages, all_classes): Move static variables to class cdef_manager. (lookup_class (std::string, bool, bool)): Move implementation to method cdef_manager::do_find_class(). (lookup_package): Move implementation to method cdef_manager::do_find_package(). (make_class): Use cdef_manager::register_class. (make_package): Use cdef_manager::register_package and cdef_manager::find_package. (cdef_class::cdef_class_rep::meta_release): Use cdef_manager::unregister_class. (cdef_method::cdef_method_rep::meta_subsref): New method. (class cdef_manager): New class. * libinterp/interpfcn/symtab.cc (symbol_table::fcn_info::fcn_info_rep::load_class_constructor): Look for classdef constructor in normal m-files. Call find_user_function() and check whether the result is a classdef constructor. If it is, stash it as a constructor and restore the previous value of function_on_path. (symbol_table::fcn_info::fcn_info_rep::load_class_method): Look for method in classdef system, using cdef_manager::find_method_symbol().
author Michael Goffioul <michael.goffioul@gmail.com>
date Mon, 11 Feb 2013 15:20:00 -0500
parents 14aa0b5a980c
children ebd2259c5df1
files libinterp/interpfcn/symtab.cc libinterp/octave-value/ov-classdef.cc libinterp/octave-value/ov-classdef.h test/classdef/test_classdef.m
diffstat 4 files changed, 333 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/interpfcn/symtab.cc	Mon Jan 28 21:56:01 2013 -0500
+++ b/libinterp/interpfcn/symtab.cc	Mon Feb 11 15:20:00 2013 -0500
@@ -36,6 +36,7 @@
 #include "dirfns.h"
 #include "input.h"
 #include "load-path.h"
+#include "ov-classdef.h"
 #include "ov-fcn.h"
 #include "ov-usr-fcn.h"
 #include "pager.h"
@@ -392,6 +393,31 @@
           class_constructors[name] = retval;
         }
     }
+  else
+    {
+      // Classdef constructors can be defined anywhere in the path, not
+      // necessarily in @-folders. Look for a normal function and load it.
+      // If the loaded function is a classdef constructor, store it as such
+      // and restore function_on_path to its previous value.
+
+      octave_value old_function_on_path = function_on_path;
+
+      octave_value maybe_cdef_ctor = find_user_function ();
+
+      if (maybe_cdef_ctor.is_defined ())
+        {
+          octave_function *fcn = maybe_cdef_ctor.function_value (true);
+
+          if (fcn && fcn->is_classdef_constructor ())
+            {
+              retval = maybe_cdef_ctor;
+
+              class_constructors[name] = retval;
+
+              function_on_path = old_function_on_path;
+            }
+        }
+    }
 
   return retval;
 }
@@ -406,43 +432,53 @@
     retval = load_class_constructor ();
   else
     {
-      std::string dir_name;
+      octave_function *cm = cdef_manager::find_method_symbol (name,
+                                                              dispatch_type);
 
-      std::string file_name = load_path::find_method (dispatch_type, name,
-                                                      dir_name);
+      if (cm)
+        retval = octave_value (cm);
 
-      if (! file_name.empty ())
+      if (! retval.is_defined ())
         {
-          octave_function *fcn = load_fcn_from_file (file_name, dir_name,
-                                                     dispatch_type);
+          std::string dir_name;
 
-          if (fcn)
+          std::string file_name = load_path::find_method (dispatch_type, name,
+                                                          dir_name);
+
+          if (! file_name.empty ())
             {
-              retval = octave_value (fcn);
+              octave_function *fcn = load_fcn_from_file (file_name, dir_name,
+                                                         dispatch_type);
 
-              class_methods[dispatch_type] = retval;
-            }
-        }
+              if (fcn)
+                {
+                  retval = octave_value (fcn);
 
-      if (retval.is_undefined ())
-        {
-          // Search parent classes
+                  class_methods[dispatch_type] = retval;
+                }
+            }
 
-          const std::list<std::string>& plist = parent_classes (dispatch_type);
-
-          std::list<std::string>::const_iterator it = plist.begin ();
-
-          while (it != plist.end ())
+          if (retval.is_undefined ())
             {
-              retval = find_method (*it);
+              // Search parent classes
 
-              if (retval.is_defined ())
+              const std::list<std::string>& plist =
+                parent_classes (dispatch_type);
+
+              std::list<std::string>::const_iterator it = plist.begin ();
+
+              while (it != plist.end ())
                 {
-                  class_methods[dispatch_type] = retval;
-                  break;
+                  retval = find_method (*it);
+
+                  if (retval.is_defined ())
+                    {
+                      class_methods[dispatch_type] = retval;
+                      break;
+                    }
+
+                  it++;
                 }
-
-              it++;
             }
         }
     }
--- a/libinterp/octave-value/ov-classdef.cc	Mon Jan 28 21:56:01 2013 -0500
+++ b/libinterp/octave-value/ov-classdef.cc	Mon Feb 11 15:20:00 2013 -0500
@@ -25,7 +25,6 @@
 #endif
 
 #include <algorithm>
-#include <map>
 
 #include "defun.h"
 #include "ov-builtin.h"
@@ -38,14 +37,12 @@
 #include "pt-misc.h"
 #include "pt-stmt.h"
 #include "pt-walk.h"
+#include "singleton-cleanup.h"
 #include "symtab.h"
 #include "toplev.h"
 
 #include "Array.cc"
 
-static std::map<std::string, cdef_class> all_classes;
-static std::map<std::string, cdef_package> all_packages;
-
 static void
 gripe_method_access (const std::string& from, const cdef_method& meth)
 {
@@ -144,41 +141,11 @@
 }
 
 static cdef_class
-lookup_class (const std::string& name, bool error_if_not_found = true)
+lookup_class (const std::string& name, bool error_if_not_found = true,
+              bool load_if_not_found = true)
 {
-  std::map<std::string, cdef_class>::iterator it = all_classes.find (name);
-
-  if (it == all_classes.end ())
-    {
-      // FIXME: implement this properly
-
-      octave_value ov_cls = symbol_table::find (name);
-
-      if (ov_cls.is_defined ())
-        it = all_classes.find (name);
-    }
-
-  if (it == all_classes.end ())
-    {
-      if (error_if_not_found)
-	error ("class not found: %s", name.c_str ());
-    }
-  else
-    {
-      cdef_class& cls = it->second;
-
-      if (! cls.is_builtin ())
-	{
-	  // FIXME: check whether a class reload is needed
-	}
-
-      if (cls.ok ())
-	return cls;
-      else
-	all_classes.erase (it);
-    }
-
-  return cdef_class ();
+  return cdef_manager::find_class (name, error_if_not_found,
+                                   load_if_not_found);
 }
 
 static cdef_class
@@ -652,7 +619,7 @@
     return cdef_class ();
 
   if (! name.empty ())
-    all_classes[name] = cls;
+    cdef_manager::register_class (cls);
 
   return cls;
 }
@@ -760,10 +727,13 @@
 
   pack.set_class (cdef_class::meta_package ());
   pack.put ("Name", nm);
-  pack.put ("ContainingPackage", to_ov (all_packages[parent]));
+  if (parent.empty ())
+    pack.put ("ContainingPackage", Matrix ());
+  else
+    pack.put ("ContainingPackage", to_ov (cdef_manager::find_package (parent)));
 
   if (! nm.empty ())
-    all_packages[nm] = pack;
+    cdef_manager::register_package (pack);
 
   return pack;
 }
@@ -2001,7 +1971,7 @@
 void
 cdef_class::cdef_class_rep::meta_release (void)
 {
-  all_classes.erase (get_name ());
+  cdef_manager::unregister_class (wrap ());
 }
 
 void
@@ -2646,24 +2616,37 @@
   return false;
 }
 
+octave_value_list
+cdef_method::cdef_method_rep::meta_subsref
+  (const std::string& type, const std::list<octave_value_list>& idx,
+   int nargout)
+{
+  octave_value_list retval;
+
+  switch (type[0])
+    {
+    case '(':
+      retval = execute (idx.front (), type.length () > 1 ? 1 : nargout, true);
+      break;
+
+    default:
+      error ("invalid meta.method indexing");
+      break;
+    }
+
+  if (! error_state)
+    {
+      if (type.length () > 1 && idx.size () > 1 && ! retval.empty ())
+	retval = retval(0).next_subsref (nargout, type, idx, 1);
+    }
+
+  return retval;
+}
+
 static cdef_package
 lookup_package (const std::string& name)
 {
-  std::map<std::string, cdef_package>::const_iterator it = all_packages.find (name);
-
-  if (it != all_packages.end ())
-    {
-      cdef_package pack = it->second;
-
-      if (pack.ok ())
-        return pack;
-      else
-        error ("invalid package: %s", name.c_str ());
-    }
-  else
-    error ("package not found: %s", name.c_str ());
-
-  return cdef_package ();
+  return cdef_manager::find_package (name);
 }
 
 static octave_value_list
@@ -2944,6 +2927,102 @@
   package_meta.install_class (meta_dynproperty, "dynproperty");
 }
 
+//----------------------------------------------------------------------------
+
+cdef_manager* cdef_manager::instance = 0;
+
+void
+cdef_manager::create_instance (void)
+{
+  instance = new cdef_manager ();
+
+  if (instance)
+    singleton_cleanup_list::add (cleanup_instance);
+}
+
+cdef_class
+cdef_manager::do_find_class (const std::string& name,
+                             bool error_if_not_found, bool load_if_not_found)
+{
+  std::map<std::string, cdef_class>::iterator it = all_classes.find (name);
+
+  if (it == all_classes.end ())
+    {
+      // FIXME: implement this properly, take package prefix into account
+
+      if (load_if_not_found)
+        {
+          octave_value ov_cls = symbol_table::find (name);
+
+          if (ov_cls.is_defined ())
+            it = all_classes.find (name);
+        }
+    }
+
+  if (it == all_classes.end ())
+    {
+      if (error_if_not_found)
+	error ("class not found: %s", name.c_str ());
+    }
+  else
+    {
+      cdef_class cls = it->second;
+
+      if (! cls.is_builtin ())
+        cls = lookup_class (cls);
+
+      if (cls.ok ())
+	return cls;
+      else
+	all_classes.erase (it);
+    }
+
+  return cdef_class ();
+}
+
+octave_function*
+cdef_manager::do_find_method_symbol (const std::string& method_name,
+                                     const std::string& class_name)
+{
+  octave_function *retval = 0;
+
+  cdef_class cls = find_class (class_name, false, false);
+
+  if (cls.ok ())
+    {
+      cdef_method meth = cls.find_method (method_name);
+
+      if (meth.ok ())
+        retval = new octave_classdef_meta (meth);
+    }
+
+  return retval;
+}
+
+cdef_package
+cdef_manager::do_find_package (const std::string& name,
+                               bool error_if_not_found)
+{
+  cdef_package retval;
+
+  std::map<std::string, cdef_package>::const_iterator it
+    = all_packages.find (name);
+
+  if (it != all_packages.end ())
+    {
+      retval = it->second;
+
+      if (! retval.ok ())
+        error ("invalid package `%s'", name.c_str ());
+    }
+  else if (error_if_not_found)
+    error ("unknown package `%s'", name.c_str ());
+
+  return retval;
+}
+
+//----------------------------------------------------------------------------
+
 DEFUN (__meta_get_package__, args, , "")
 {
   octave_value retval;
--- a/libinterp/octave-value/ov-classdef.h	Mon Jan 28 21:56:01 2013 -0500
+++ b/libinterp/octave-value/ov-classdef.h	Mon Feb 11 15:20:00 2013 -0500
@@ -23,6 +23,7 @@
 #if !defined (octave_classdef_h)
 #define octave_classdef_h 1
 
+#include <map>
 #include <set>
 #include <string>
 
@@ -1039,6 +1040,13 @@
 
     bool is_constructor (void) const;
 
+    octave_value_list
+    meta_subsref (const std::string& type,
+                  const std::list<octave_value_list>& idx, int nargout);
+
+    bool meta_is_postfix_index_handled (char type) const
+      { return (type == '(' || type == '.'); }
+
   private:
     cdef_method_rep (const cdef_method_rep& m)
       : cdef_meta_object_rep (m), function (m.function) { }
@@ -1460,6 +1468,133 @@
 
 OCTINTERP_API void install_classdef (void);
 
+class
+cdef_manager
+{
+public:
+
+  static cdef_class find_class (const std::string& name,
+                                bool error_if_not_found = true,
+                                bool load_if_not_found = true)
+    {
+      if (instance_ok ())
+        return instance->do_find_class (name, error_if_not_found,
+                                        load_if_not_found);
+
+      return cdef_class ();
+    }
+
+  static octave_function* find_method_symbol (const std::string& method_name,
+                                              const std::string& class_name)
+    {
+      if (instance_ok ())
+        return instance->do_find_method_symbol (method_name, class_name);
+
+      return 0;
+    }
+
+  static cdef_package find_package (const std::string& name,
+                                    bool error_if_not_found = true)
+    {
+      if (instance_ok ())
+        return instance->do_find_package (name, error_if_not_found);
+
+      return cdef_package ();
+    }
+
+  static void register_class (const cdef_class& cls)
+    {
+      if (instance_ok ())
+        instance->do_register_class (cls);
+    }
+
+  static void unregister_class (const cdef_class& cls)
+    {
+      if (instance_ok ())
+        instance->do_unregister_class (cls);
+    }
+
+  static void register_package (const cdef_package& pkg)
+    {
+      if (instance_ok ())
+        instance->do_register_package (pkg);
+    }
+
+  static void unregister_package (const cdef_package& pkg)
+    {
+      if (instance_ok ())
+        instance->do_unregister_package (pkg);
+    }
+
+private:
+
+  cdef_manager (void) { }
+
+  cdef_manager (const cdef_manager&);
+
+  cdef_manager& operator = (const cdef_manager&);
+
+  ~cdef_manager (void) { }
+
+  static void create_instance (void);
+
+  static bool instance_ok (void)
+    {
+      bool retval = true;
+
+      if (! instance)
+        create_instance ();
+
+      if (! instance)
+        {
+          ::error ("unable to create cdef_manager!");
+
+          retval = false;
+        }
+
+      return retval;
+    }
+
+  static void cleanup_instance (void)
+    {
+      delete instance;
+
+      instance = 0;
+    }
+
+  cdef_class do_find_class (const std::string& name, bool error_if_not_found,
+                            bool load_if_not_found);
+
+  octave_function* do_find_method_symbol (const std::string& method_name,
+                                          const std::string& class_name);
+
+  cdef_package do_find_package (const std::string& name,
+                                bool error_if_not_found);
+
+  void do_register_class (const cdef_class& cls)
+    { all_classes[cls.get_name ()] = cls; }
+
+  void do_unregister_class (const cdef_class& cls)
+    { all_classes.erase(cls.get_name ()); }
+
+  void do_register_package (const cdef_package& pkg)
+    { all_packages[pkg.get_name ()] = pkg; }
+
+  void do_unregister_package (const cdef_package& pkg)
+    { all_packages.erase(pkg.get_name ()); }
+
+private:
+
+  // The single cdef_manager instance
+  static cdef_manager *instance;
+
+  // All registered/loaded classes
+  std::map<std::string, cdef_class> all_classes;
+
+  // All registered/loaded packages
+  std::map<std::string, cdef_package> all_packages;
+};
+
 #endif
 
 /*
--- a/test/classdef/test_classdef.m	Mon Jan 28 21:56:01 2013 -0500
+++ b/test/classdef/test_classdef.m	Mon Feb 11 15:20:00 2013 -0500
@@ -47,8 +47,7 @@
 %!assert (p.rate, 4.0);
 %!assert (p.principle, 50e3);
 %!assert (p.amount, amt, eps ())
-%!xtest
-%! assert (amount (p), amt, eps ())
+%!assert (amount (p), amt, eps ())
 %!xtest
 %! assert (properties (p), {'rate'; 'term'; 'principle'})
 %!xtest