changeset 16347:bf8397caeff1

allow add_input_event_hook to accept function handles * input.cc (base_hook_function, hook_function, named_hook_function, fcn_handle_hook_function): New classes. (input_event_hook, Fadd_input_event_hook, Fremove_input_event_hook): Use hook_function object. (Fadd_input_event_hook): Return hook_function ID. (Fremove_input_event_hook): Use ID to search for hook_function. * input.h, input.cc (remove_input_event_hook_functions): New function. * toplev.cc (do_octave_atexit): Call it.
author John W. Eaton <jwe@octave.org>
date Thu, 21 Mar 2013 02:04:12 -0400
parents fe112e09d799
children 072b3e6504c4
files libinterp/interpfcn/input.cc libinterp/interpfcn/input.h libinterp/interpfcn/toplev.cc
diffstat 3 files changed, 204 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/interpfcn/input.cc
+++ b/libinterp/interpfcn/input.cc
@@ -56,6 +56,7 @@
 #include "oct-hist.h"
 #include "toplev.h"
 #include "oct-obj.h"
+#include "ov-fcn-handle.h"
 #include "pager.h"
 #include "parse.h"
 #include "pathlen.h"
@@ -1047,10 +1048,179 @@
   return retval;
 }
 
-typedef std::map<std::string, octave_value> hook_fcn_map_type;
+class
+base_hook_function
+{
+public:
+
+  friend class hook_function;
+
+  base_hook_function (void) : count (1) { }
+
+  base_hook_function (const base_hook_function&) : count (1) { }
+
+  virtual ~base_hook_function (void) { }
+
+  virtual std::string id (void) { return std::string (); }
+
+  virtual bool is_valid (void) { return false; }
+
+  virtual void eval (void) { }
+
+protected:
+
+  size_t count;
+};
+
+class
+hook_function
+{
+public:
+
+  hook_function (void)
+  {
+    static base_hook_function nil_rep;
+    rep = &nil_rep;
+    rep->count++;
+  }
+
+  hook_function (const octave_value& f,
+                 const octave_value& d = octave_value ());
+
+  ~hook_function (void)
+  {
+    if (--rep->count == 0)
+      delete rep;
+  }
+
+  hook_function (const hook_function& hf)
+    : rep (hf.rep)
+  {
+    rep->count++;
+  }
+
+  hook_function& operator = (const hook_function& hf)
+  {
+    if (rep != hf.rep)
+      {
+        if (--rep->count == 0)
+          delete rep;
+
+        rep = hf.rep;
+        rep->count++;
+      }
+
+    return *this;
+  }
+
+  std::string id (void) { return rep->id (); }
+
+  bool is_valid (void) { return rep->is_valid (); }
+
+  void eval (void) { rep->eval (); }
+
+private:
+
+  base_hook_function *rep;
+};
+
+class
+named_hook_function : public base_hook_function
+{
+public:
+
+  named_hook_function (const std::string& n, const octave_value& d)
+    : name (n), data (d)
+  { }
+
+  void eval (void)
+  {
+    if (data.is_defined ())
+      feval (name, data, 0);
+    else
+      feval (name, octave_value_list (), 0);
+  }
+
+  std::string id (void) { return name; }
+
+  bool is_valid (void) { return is_valid_function (name); }
+
+private:
+
+  std::string name;
+
+  octave_value data;
+};
+
+class
+fcn_handle_hook_function : public base_hook_function
+{
+public:
+
+  fcn_handle_hook_function (const octave_value& fh_arg, const octave_value& d)
+    : ident (), valid (false), fcn_handle (fh_arg), data (d)
+  {
+    octave_fcn_handle *fh = fcn_handle.fcn_handle_value (true);
+
+    if (fh)
+      {
+        valid = true;
+
+        std::ostringstream buf;
+        buf << fh;
+        ident = fh->fcn_name () + ":" + buf.str ();
+      }
+  }
+
+  void eval (void)
+  {
+    if (data.is_defined ())
+      fcn_handle.do_multi_index_op (0, data);
+    else
+      fcn_handle.do_multi_index_op (0, octave_value_list ());
+  }
+
+  std::string id (void) { return ident; }
+
+  bool is_valid (void) { return valid; }
+
+private:
+
+  std::string ident;
+
+  bool valid;
+
+  octave_value fcn_handle;
+
+  octave_value data;
+};
+
+hook_function::hook_function (const octave_value& f, const octave_value& d)
+{
+  if (f.is_string ())
+    {
+      std::string name = f.string_value ();
+
+      rep = new named_hook_function (name, d);
+    }
+  else if (f.is_function_handle ())
+    {
+      rep = new fcn_handle_hook_function (f, d);
+    }
+  else
+    error ("invalid hook function");
+}
+
+typedef std::map<std::string, hook_function> hook_fcn_map_type;
 
 static hook_fcn_map_type hook_fcn_map;
 
+void
+remove_input_event_hook_functions (void)
+{
+  hook_fcn_map.clear ();
+}
+
 static int
 input_event_hook (void)
 {
@@ -1058,18 +1228,13 @@
 
   while (p != hook_fcn_map.end ())
     {
-      std::string hook_fcn = p->first;
-      octave_value user_data = p->second;
+      std::string hook_fcn_id = p->first;
+      hook_function hook_fcn = p->second;
 
       hook_fcn_map_type::iterator q = p++;
 
-      if (is_valid_function (hook_fcn))
-        {
-          if (user_data.is_defined ())
-            feval (hook_fcn, user_data, 0);
-          else
-            feval (hook_fcn, octave_value_list (), 0);
-        }
+      if (hook_fcn.is_valid ())
+        hook_fcn.eval ();
       else
         hook_fcn_map.erase (q);
     }
@@ -1082,8 +1247,8 @@
 
 DEFUN (add_input_event_hook, args, ,
   "-*- texinfo -*-\n\
-@deftypefn  {Built-in Function} {} add_input_event_hook (@var{fcn})\n\
-@deftypefnx {Built-in Function} {} add_input_event_hook (@var{fcn}, @var{data})\n\
+@deftypefn  {Built-in Function} {@var{id} =} add_input_event_hook (@var{fcn})\n\
+@deftypefnx {Built-in Function} {@var{id} =} add_input_event_hook (@var{fcn}, @var{data})\n\
 Add the named function @var{fcn} to the list of functions to call\n\
 periodically when Octave is waiting for input.  The function should\n\
 have the form\n\
@@ -1094,10 +1259,13 @@
 \n\
 If @var{data} is omitted, Octave calls the function without any\n\
 arguments.\n\
+\n\
+The returned identifier may be used to remove the function handle from\n\
+the list of input hook functions.\n\
 @seealso{remove_input_event_hook}\n\
 @end deftypefn")
 {
-  octave_value_list retval;
+  octave_value retval;
 
   int nargin = args.length ();
 
@@ -1108,17 +1276,19 @@
       if (nargin == 2)
         user_data = args(1);
 
-      std::string hook_fcn = args(0).string_value ();
+      hook_function hook_fcn (args(0), user_data);
 
       if (! error_state)
         {
           if (hook_fcn_map.empty ())
             command_editor::add_event_hook (input_event_hook);
 
-          hook_fcn_map[hook_fcn] = user_data;
+          hook_fcn_map[hook_fcn.id ()] = hook_fcn;
+
+          retval = hook_fcn.id ();
         }
       else
-        error ("add_input_event_hook: expecting string as first arg");
+        error ("add_input_event_hook: expecting function handle or character string as first argument");
     }
   else
     print_usage ();
@@ -1128,9 +1298,11 @@
 
 DEFUN (remove_input_event_hook, args, ,
   "-*- texinfo -*-\n\
-@deftypefn {Built-in Function} {} remove_input_event_hook (@var{fcn})\n\
-Remove the named function @var{fcn} from the list of functions to call\n\
-periodically when Octave is waiting for input.\n\
+@deftypefn {Built-in Function} {} remove_input_event_hook (@var{name})\n\
+@deftypefnx {Built-in Function} {} remove_input_event_hook (@var{fcn_id})\n\
+Remove the named function or function handle with the given identifier\n\
+from the list of functions to call periodically when Octave is waiting\n\
+for input.\n\
 @seealso{add_input_event_hook}\n\
 @end deftypefn")
 {
@@ -1138,25 +1310,27 @@
 
   int nargin = args.length ();
 
-  if (nargin == 1)
+  if (nargin == 1 || nargin == 2)
     {
-      std::string hook_fcn = args(0).string_value ();
+      std::string hook_fcn_id = args(0).string_value ();
+
+      bool warn = (nargin < 2);
 
       if (! error_state)
         {
-          hook_fcn_map_type::iterator p = hook_fcn_map.find (hook_fcn);
+          hook_fcn_map_type::iterator p = hook_fcn_map.find (hook_fcn_id);
 
           if (p != hook_fcn_map.end ())
             hook_fcn_map.erase (p);
-          else
-            error ("remove_input_event_hook: %s not found in list",
-                   hook_fcn.c_str ());
+          else if (warn)
+            warning ("remove_input_event_hook: %s not found in list",
+                     hook_fcn_id.c_str ());
 
           if (hook_fcn_map.empty ())
             command_editor::remove_event_hook (input_event_hook);
         }
       else
-        error ("remove_input_event_hook: expecting string as first arg");
+        error ("remove_input_event_hook: argument not valid as a hook function name or id");
     }
   else
     print_usage ();
--- a/libinterp/interpfcn/input.h
+++ b/libinterp/interpfcn/input.h
@@ -59,6 +59,8 @@
 
 extern octave_value do_keyboard (const octave_value_list& args = octave_value_list ());
 
+extern void remove_input_event_hook_functions (void);
+
 extern std::string VPS4;
 
 extern char Vfilemarker;
--- a/libinterp/interpfcn/toplev.cc
+++ b/libinterp/interpfcn/toplev.cc
@@ -1021,6 +1021,8 @@
 {
   static bool deja_vu = false;
 
+  OCTAVE_SAFE_CALL (remove_input_event_hook_functions, ());
+
   while (! octave_atexit_functions.empty ())
     {
       std::string fcn = octave_atexit_functions.front ();