changeset 14136:20cb178716ee

Make nargin and nargout behave more like Matlab's (Bug #32700) * ov-usr-fcn.cc: Include "ov-fcn-handle.h". (Fnargout, Fnargin) nargout can be given a function handle as well as a string. Allow nargout(fcn) works in the top level. Make inline functions always return 1. Make anonymous functions always return -1. Make functions with varargout optionally preceded by named outputs return -(length_output_list_including_the_varargout).
author Iain Murray <iain@iainmurray.net>
date Mon, 02 Jan 2012 12:49:52 -0500
parents 6a1fe83fe129
children a74954d70c18
files src/ov-usr-fcn.cc
diffstat 1 files changed, 91 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/src/ov-usr-fcn.cc	Sat Feb 26 18:15:48 2011 -0500
+++ b/src/ov-usr-fcn.cc	Mon Jan 02 12:49:52 2012 -0500
@@ -48,6 +48,7 @@
 #include "utils.h"
 #include "parse.h"
 #include "variables.h"
+#include "ov-fcn-handle.h"
 
 // Whether to optimize subsasgn method calls.
 static bool Voptimize_subsasgn_calls = true;
@@ -601,12 +602,15 @@
 DEFUN (nargin, args, ,
   "-*- texinfo -*-\n\
 @deftypefn  {Built-in Function} {} nargin ()\n\
-@deftypefnx {Built-in Function} {} nargin (@var{fcn_name})\n\
+@deftypefnx {Built-in Function} {} nargin (@var{fcn})\n\
 Within a function, return the number of arguments passed to the function.\n\
 At the top level, return the number of command line arguments passed to\n\
-Octave.  If called with the optional argument @var{fcn_name}, return the\n\
-maximum number of arguments the named function can accept, or -1 if the\n\
-function accepts a variable number of arguments.\n\
+Octave.\n\
+\n\
+If called with the optional argument @var{fcn}, a function name or handle,\n\
+return the declared number of arguments that the function can accept.\n\
+If the last argument is @var{varargin} the returned value is negative.\n\
+This feature does not work on builtin functions.\n\
 @seealso{nargout, varargin, varargout}\n\
 @end deftypefn")
 {
@@ -616,30 +620,37 @@
 
   if (nargin == 1)
     {
-      std::string fname = args(0).string_value ();
+      octave_value func = args(0);
 
-      if (! error_state)
+      if (func.is_string ())
         {
-          octave_value fcn_val = symbol_table::find_user_function (fname);
+          std::string name = func.string_value ();
+          func = symbol_table::find_function (name);
+          if (func.is_undefined ())
+            error ("nargout: invalid function name: %s", name.c_str ());
+        }
 
-          octave_user_function *fcn = fcn_val.user_function_value (true);
+      octave_function *fcn_val = func.function_value ();
+      if (fcn_val)
+        {
+          octave_user_function *fcn = fcn_val->user_function_value (true);
 
           if (fcn)
             {
+              tree_parameter_list *param_list = fcn->parameter_list ();
+
+              retval = param_list ? param_list->length () : 0;
               if (fcn->takes_varargs ())
-                retval = -1;
-              else
-                {
-                  tree_parameter_list *param_list = fcn->parameter_list ();
-
-                  retval = param_list ? param_list->length () : 0;
-                }
+                retval = -1 - retval;
             }
           else
-            error ("nargin: invalid function");
+            {
+              // Matlab gives up for histc, so maybe it's ok we give up somtimes too.
+              error ("nargin: nargin information not available for builtin functions");
+            }
         }
       else
-        error ("nargin: FCN_NAME must be a string");
+        error ("nargin: FCN must be a string or function handle");
     }
   else if (nargin == 0)
     {
@@ -657,11 +668,12 @@
 DEFUN (nargout, args, ,
   "-*- texinfo -*-\n\
 @deftypefn  {Built-in Function} {} nargout ()\n\
-@deftypefnx {Built-in Function} {} nargout (@var{fcn_name})\n\
+@deftypefnx {Built-in Function} {} nargout (@var{fcn})\n\
 Within a function, return the number of values the caller expects to\n\
-receive.  If called with the optional argument @var{fcn_name}, return the\n\
-maximum number of values the named function can produce, or -1 if the\n\
-function can produce a variable number of values.\n\
+receive.  If called with the optional argument @var{fcn}, a function\n\
+name or handle, return the number of declared output values that the\n\
+function can produce. If the final output argument is @var{varargout}\n\
+the returned value is negative.\n\
 \n\
 For example,\n\
 \n\
@@ -680,7 +692,23 @@
 will cause @code{nargout} to return 2 inside the function\n\
 @code{f}.\n\
 \n\
-At the top level, @code{nargout} is undefined.\n\
+In the second usage,\n\
+\n\
+@example\n\
+nargout (@@histc) \% or nargout ('histc')\n\
+@end example\n\
+\n\
+will return 2, because @code{histc} has two outputs, whereas\n\
+\n\
+@example\n\
+nargout (@@deal)\n\
+@end example\n\
+\n\
+will return -1, because @code{deal} has a variable number of outputs.\n\
+\n\
+At the top level, @code{nargout} with no argument is undefined.\n\
+@code{nargout} does not work on builtin functions.\n\
+@code{nargout} returns -1 for all anonymous functions.\n\
 @seealso{nargin, varargin, varargout}\n\
 @end deftypefn")
 {
@@ -690,30 +718,58 @@
 
   if (nargin == 1)
     {
-      std::string fname = args(0).string_value ();
+      octave_value func = args(0);
 
-      if (! error_state)
+      if (func.is_string ())
+        {
+          std::string name = func.string_value ();
+          func = symbol_table::find_function (name);
+          if (func.is_undefined ())
+            error ("nargout: invalid function name: %s", name.c_str ());
+        }
+
+      if (func.is_inline_function ())
         {
-          octave_value fcn_val = symbol_table::find_user_function (fname);
+          retval = 1;
+          return retval;
+        }
+
+      if (func.is_function_handle ())
+        {
+          octave_fcn_handle *fh = func.fcn_handle_value ();
+          std::string fh_nm = fh->fcn_name ();
 
-          octave_user_function *fcn = fcn_val.user_function_value (true);
+          if (fh_nm == octave_fcn_handle::anonymous)
+            {
+              retval = -1;
+              return retval;
+            }
+        }
+
+      octave_function *fcn_val = func.function_value ();
+      if (fcn_val)
+        {
+          octave_user_function *fcn = fcn_val->user_function_value (true);
 
           if (fcn)
             {
+              tree_parameter_list *ret_list = fcn->return_list ();
+          
+              retval = ret_list ? ret_list->length () : 0;
+
               if (fcn->takes_var_return ())
-                retval = -1;
-              else
-                {
-                  tree_parameter_list *ret_list = fcn->return_list ();
-
-                  retval = ret_list ? ret_list->length () : 0;
-                }
+                retval = -1 - retval;
             }
           else
-            error ("nargout: invalid function");
+            {
+              // JWE said this information is not available (currently, 2011-03-10)
+              // without making intrusive changes to Octave.
+              // Matlab gives up for histc, so maybe it's ok we give up somtimes too.
+              error ("nargout: nargout information not available for builtin functions.");
+            }
         }
       else
-        error ("nargout: FCN_NAME must be a string");
+        error ("nargout: FCN must be a string or function handle");
     }
   else if (nargin == 0)
     {