ov-usr-fcn.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 1996-2012 John W. Eaton
00004 
00005 This file is part of Octave.
00006 
00007 Octave is free software; you can redistribute it and/or modify it
00008 under the terms of the GNU General Public License as published by the
00009 Free Software Foundation; either version 3 of the License, or (at your
00010 option) any later version.
00011 
00012 Octave is distributed in the hope that it will be useful, but WITHOUT
00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00015 for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Octave; see the file COPYING.  If not, see
00019 <http://www.gnu.org/licenses/>.
00020 
00021 */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #include <sstream>
00028 
00029 #include "str-vec.h"
00030 
00031 #include <defaults.h>
00032 #include "Cell.h"
00033 #include "defun.h"
00034 #include "error.h"
00035 #include "gripes.h"
00036 #include "input.h"
00037 #include "oct-obj.h"
00038 #include "ov-usr-fcn.h"
00039 #include "ov.h"
00040 #include "pager.h"
00041 #include "pt-eval.h"
00042 #include "pt-jump.h"
00043 #include "pt-misc.h"
00044 #include "pt-pr-code.h"
00045 #include "pt-stmt.h"
00046 #include "pt-walk.h"
00047 #include "symtab.h"
00048 #include "toplev.h"
00049 #include "unwind-prot.h"
00050 #include "utils.h"
00051 #include "parse.h"
00052 #include "profiler.h"
00053 #include "variables.h"
00054 
00055 // Whether to optimize subsasgn method calls.
00056 static bool Voptimize_subsasgn_calls = true;
00057 
00058 // User defined scripts.
00059 
00060 DEFINE_OCTAVE_ALLOCATOR (octave_user_script);
00061 
00062 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_user_script,
00063                                      "user-defined script",
00064                                      "user-defined script");
00065 
00066 octave_user_script::octave_user_script (void)
00067   : octave_user_code (), cmd_list (0), file_name (),
00068     t_parsed (static_cast<time_t> (0)),
00069     t_checked (static_cast<time_t> (0)),
00070     call_depth (-1)
00071 { }
00072 
00073 octave_user_script::octave_user_script (const std::string& fnm,
00074                                         const std::string& nm,
00075                                         tree_statement_list *cmds,
00076                                         const std::string& ds)
00077   : octave_user_code (nm, ds), cmd_list (cmds), file_name (fnm),
00078     t_parsed (static_cast<time_t> (0)),
00079     t_checked (static_cast<time_t> (0)),
00080     call_depth (-1)
00081 {
00082   if (cmd_list)
00083     cmd_list->mark_as_script_body ();
00084 }
00085 
00086 octave_user_script::octave_user_script (const std::string& fnm,
00087                                         const std::string& nm,
00088                                         const std::string& ds)
00089   : octave_user_code (nm, ds), cmd_list (0), file_name (fnm),
00090     t_parsed (static_cast<time_t> (0)),
00091     t_checked (static_cast<time_t> (0)),
00092     call_depth (-1)
00093 { }
00094 
00095 octave_user_script::~octave_user_script (void)
00096 {
00097   delete cmd_list;
00098 }
00099 
00100 octave_value_list
00101 octave_user_script::subsref (const std::string&,
00102                              const std::list<octave_value_list>&, int)
00103 {
00104   octave_value_list retval;
00105 
00106   ::error ("invalid use of script %s in index expression", file_name.c_str ());
00107 
00108   return retval;
00109 }
00110 
00111 octave_value_list
00112 octave_user_script::do_multi_index_op (int nargout,
00113                                        const octave_value_list& args)
00114 {
00115   octave_value_list retval;
00116 
00117   unwind_protect frame;
00118 
00119   if (! error_state)
00120     {
00121       if (args.length () == 0 && nargout == 0)
00122         {
00123           if (cmd_list)
00124             {
00125               frame.protect_var (call_depth);
00126               call_depth++;
00127 
00128               if (call_depth < Vmax_recursion_depth)
00129                 {
00130                   octave_call_stack::push (this);
00131 
00132                   frame.add_fcn (octave_call_stack::pop);
00133 
00134                   frame.protect_var (tree_evaluator::statement_context);
00135                   tree_evaluator::statement_context = tree_evaluator::script;
00136 
00137                   BEGIN_PROFILER_BLOCK (profiler_name ())
00138                   cmd_list->accept (*current_evaluator);
00139                   END_PROFILER_BLOCK
00140 
00141                   if (tree_return_command::returning)
00142                     tree_return_command::returning = 0;
00143 
00144                   if (tree_break_command::breaking)
00145                     tree_break_command::breaking--;
00146 
00147                   if (error_state)
00148                     octave_call_stack::backtrace_error_message ();
00149                 }
00150               else
00151                 ::error ("max_recursion_depth exceeded");
00152             }
00153         }
00154       else
00155         error ("invalid call to script %s", file_name.c_str ());
00156     }
00157 
00158   return retval;
00159 }
00160 
00161 void
00162 octave_user_script::accept (tree_walker& tw)
00163 {
00164   tw.visit_octave_user_script (*this);
00165 }
00166 
00167 // User defined functions.
00168 
00169 DEFINE_OCTAVE_ALLOCATOR (octave_user_function);
00170 
00171 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_user_function,
00172                                      "user-defined function",
00173                                      "user-defined function");
00174 
00175 // Ugh.  This really needs to be simplified (code/data?
00176 // extrinsic/intrinsic state?).
00177 
00178 octave_user_function::octave_user_function
00179   (symbol_table::scope_id sid, tree_parameter_list *pl,
00180    tree_parameter_list *rl, tree_statement_list *cl)
00181   : octave_user_code (std::string (), std::string ()),
00182     param_list (pl), ret_list (rl), cmd_list (cl),
00183     lead_comm (), trail_comm (), file_name (),
00184     location_line (0), location_column (0),
00185     parent_name (), t_parsed (static_cast<time_t> (0)),
00186     t_checked (static_cast<time_t> (0)),
00187     system_fcn_file (false), call_depth (-1),
00188     num_named_args (param_list ? param_list->length () : 0),
00189     subfunction (false), inline_function (false),
00190     anonymous_function (false), class_constructor (false),
00191     class_method (false), parent_scope (-1), local_scope (sid),
00192     curr_unwind_protect_frame (0)
00193 {
00194   if (cmd_list)
00195     cmd_list->mark_as_function_body ();
00196 
00197   if (local_scope >= 0)
00198     symbol_table::set_curr_fcn (this, local_scope);
00199 }
00200 
00201 octave_user_function::~octave_user_function (void)
00202 {
00203   delete param_list;
00204   delete ret_list;
00205   delete cmd_list;
00206   delete lead_comm;
00207   delete trail_comm;
00208 
00209   symbol_table::erase_scope (local_scope);
00210 }
00211 
00212 octave_user_function *
00213 octave_user_function::define_ret_list (tree_parameter_list *t)
00214 {
00215   ret_list = t;
00216 
00217   return this;
00218 }
00219 
00220 void
00221 octave_user_function::stash_fcn_file_name (const std::string& nm)
00222 {
00223   file_name = nm;
00224 }
00225 
00226 std::string
00227 octave_user_function::profiler_name (void) const
00228 {
00229   std::ostringstream result;
00230 
00231   if (is_inline_function ())
00232     result << "inline@" << fcn_file_name ()
00233            << ":" << location_line << ":" << location_column;
00234   else if (is_anonymous_function ())
00235     result << "anonymous@" << fcn_file_name ()
00236            << ":" << location_line << ":" << location_column;
00237   else if (is_subfunction ())
00238     result << parent_fcn_name () << ">" << name ();
00239   else
00240     result << name ();
00241 
00242   return result.str ();
00243 }
00244 
00245 void
00246 octave_user_function::mark_as_system_fcn_file (void)
00247 {
00248   if (! file_name.empty ())
00249     {
00250       // We really should stash the whole path to the file we found,
00251       // when we looked it up, to avoid possible race conditions...
00252       // FIXME
00253       //
00254       // We probably also don't need to get the library directory
00255       // every time, but since this function is only called when the
00256       // function file is parsed, it probably doesn't matter that
00257       // much.
00258 
00259       std::string ff_name = fcn_file_in_path (file_name);
00260 
00261       if (Vfcn_file_dir == ff_name.substr (0, Vfcn_file_dir.length ()))
00262         system_fcn_file = 1;
00263     }
00264   else
00265     system_fcn_file = 0;
00266 }
00267 
00268 bool
00269 octave_user_function::takes_varargs (void) const
00270 {
00271   return (param_list && param_list->takes_varargs ());
00272 }
00273 
00274 bool
00275 octave_user_function::takes_var_return (void) const
00276 {
00277   return (ret_list && ret_list->takes_varargs ());
00278 }
00279 
00280 void
00281 octave_user_function::lock_subfunctions (void)
00282 {
00283   symbol_table::lock_subfunctions (local_scope);
00284 }
00285 
00286 void
00287 octave_user_function::unlock_subfunctions (void)
00288 {
00289   symbol_table::unlock_subfunctions (local_scope);
00290 }
00291 
00292 octave_value_list
00293 octave_user_function::all_va_args (const octave_value_list& args)
00294 {
00295   octave_value_list retval;
00296 
00297   octave_idx_type n = args.length () - num_named_args;
00298 
00299   if (n > 0)
00300     retval = args.slice (num_named_args, n);
00301 
00302   return retval;
00303 }
00304 
00305 octave_value_list
00306 octave_user_function::subsref (const std::string& type,
00307                                const std::list<octave_value_list>& idx,
00308                                int nargout)
00309 {
00310   return octave_user_function::subsref (type, idx, nargout, 0);
00311 }
00312 
00313 octave_value_list
00314 octave_user_function::subsref (const std::string& type,
00315                                const std::list<octave_value_list>& idx,
00316                                int nargout, const std::list<octave_lvalue>* lvalue_list)
00317 {
00318   octave_value_list retval;
00319 
00320   switch (type[0])
00321     {
00322     case '(':
00323       {
00324         int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout;
00325 
00326         retval = do_multi_index_op (tmp_nargout, idx.front (),
00327                                     idx.size () == 1 ? lvalue_list : 0);
00328       }
00329       break;
00330 
00331     case '{':
00332     case '.':
00333       {
00334         std::string nm = type_name ();
00335         error ("%s cannot be indexed with %c", nm.c_str (), type[0]);
00336       }
00337       break;
00338 
00339     default:
00340       panic_impossible ();
00341     }
00342 
00343   // FIXME -- perhaps there should be an
00344   // octave_value_list::next_subsref member function?  See also
00345   // octave_builtin::subsref.
00346 
00347   if (idx.size () > 1)
00348     retval = retval(0).next_subsref (nargout, type, idx);
00349 
00350   return retval;
00351 }
00352 
00353 octave_value_list
00354 octave_user_function::do_multi_index_op (int nargout,
00355                                          const octave_value_list& args)
00356 {
00357   return do_multi_index_op (nargout, args, 0);
00358 }
00359 
00360 octave_value_list
00361 octave_user_function::do_multi_index_op (int nargout,
00362                                          const octave_value_list& args,
00363                                          const std::list<octave_lvalue>* lvalue_list)
00364 {
00365   octave_value_list retval;
00366 
00367   if (error_state)
00368     return retval;
00369 
00370   if (! cmd_list)
00371     return retval;
00372 
00373   int nargin = args.length ();
00374 
00375   unwind_protect frame;
00376 
00377   frame.protect_var (call_depth);
00378   call_depth++;
00379 
00380   if (call_depth >= Vmax_recursion_depth)
00381     {
00382       ::error ("max_recursion_depth exceeded");
00383       return retval;
00384     }
00385 
00386   // Save old and set current symbol table context, for
00387   // eval_undefined_error().
00388 
00389   octave_call_stack::push (this, local_scope, call_depth);
00390   frame.add_fcn (octave_call_stack::pop);
00391 
00392   if (call_depth > 0)
00393     {
00394       symbol_table::push_context ();
00395 
00396       frame.add_fcn (symbol_table::pop_context);
00397     }
00398 
00399   string_vector arg_names = args.name_tags ();
00400 
00401   if (param_list && ! param_list->varargs_only ())
00402     {
00403       param_list->define_from_arg_vector (args);
00404       if (error_state)
00405         return retval;
00406     }
00407 
00408   // Force parameter list to be undefined when this function exits.
00409   // Doing so decrements the reference counts on the values of local
00410   // variables that are also named function parameters.
00411 
00412   if (param_list)
00413     frame.add_method (param_list, &tree_parameter_list::undefine);
00414 
00415   // Force return list to be undefined when this function exits.
00416   // Doing so decrements the reference counts on the values of local
00417   // variables that are also named values returned by this function.
00418 
00419   if (ret_list)
00420     frame.add_method (ret_list, &tree_parameter_list::undefine);
00421 
00422   if (call_depth == 0)
00423     {
00424       // Force symbols to be undefined again when this function
00425       // exits.
00426       //
00427       // This cleanup function is added to the unwind_protect stack
00428       // after the calls to clear the parameter lists so that local
00429       // variables will be cleared before the parameter lists are
00430       // cleared.  That way, any function parameters that have been
00431       // declared global will be unmarked as global before they are
00432       // undefined by the clear_param_list cleanup function.
00433 
00434       frame.add_fcn (symbol_table::clear_variables);
00435     }
00436 
00437   bind_automatic_vars (arg_names, nargin, nargout, all_va_args (args),
00438                        lvalue_list);
00439 
00440   bool echo_commands = (Vecho_executing_commands & ECHO_FUNCTIONS);
00441 
00442   if (echo_commands)
00443     print_code_function_header ();
00444 
00445   // Set pointer to the current unwind_protect frame to allow
00446   // certain builtins register simple cleanup in a very optimized manner.
00447   // This is *not* intended as a general-purpose on-cleanup mechanism,
00448   frame.protect_var (curr_unwind_protect_frame);
00449   curr_unwind_protect_frame = &frame;
00450 
00451   // Evaluate the commands that make up the function.
00452 
00453   frame.protect_var (tree_evaluator::statement_context);
00454   tree_evaluator::statement_context = tree_evaluator::function;
00455 
00456   bool special_expr = (is_inline_function () || is_anonymous_function ());
00457 
00458   BEGIN_PROFILER_BLOCK (profiler_name ())
00459 
00460   if (special_expr)
00461     {
00462       assert (cmd_list->length () == 1);
00463 
00464       tree_statement *stmt = 0;
00465 
00466       if ((stmt = cmd_list->front ())
00467           && stmt->is_expression ())
00468         {
00469           tree_expression *expr = stmt->expression ();
00470 
00471           retval = expr->rvalue (nargout);
00472         }
00473     }
00474   else
00475     cmd_list->accept (*current_evaluator);
00476 
00477   END_PROFILER_BLOCK
00478 
00479   if (echo_commands)
00480     print_code_function_trailer ();
00481 
00482   if (tree_return_command::returning)
00483     tree_return_command::returning = 0;
00484 
00485   if (tree_break_command::breaking)
00486     tree_break_command::breaking--;
00487 
00488   if (error_state)
00489     {
00490       octave_call_stack::backtrace_error_message ();
00491       return retval;
00492     }
00493 
00494   // Copy return values out.
00495 
00496   if (ret_list && ! special_expr)
00497     {
00498       ret_list->initialize_undefined_elements (my_name, nargout, Matrix ());
00499 
00500       Cell varargout;
00501 
00502       if (ret_list->takes_varargs ())
00503         {
00504           octave_value varargout_varval = symbol_table::varval ("varargout");
00505 
00506           if (varargout_varval.is_defined ())
00507             {
00508               varargout = varargout_varval.cell_value ();
00509 
00510               if (error_state)
00511                 error ("expecting varargout to be a cell array object");
00512             }
00513         }
00514 
00515       if (! error_state)
00516         retval = ret_list->convert_to_const_vector (nargout, varargout);
00517     }
00518 
00519   return retval;
00520 }
00521 
00522 void
00523 octave_user_function::accept (tree_walker& tw)
00524 {
00525   tw.visit_octave_user_function (*this);
00526 }
00527 
00528 bool
00529 octave_user_function::subsasgn_optimization_ok (void)
00530 {
00531   bool retval = false;
00532   if (Voptimize_subsasgn_calls
00533       && param_list->length () > 0 && ! param_list->varargs_only ()
00534       && ret_list->length () == 1 && ! ret_list->takes_varargs ())
00535     {
00536       tree_identifier *par1 = param_list->front ()->ident ();
00537       tree_identifier *ret1 = ret_list->front ()->ident ();
00538       retval = par1->name () == ret1->name ();
00539     }
00540 
00541   return retval;
00542 }
00543 
00544 #if 0
00545 void
00546 octave_user_function::print_symtab_info (std::ostream& os) const
00547 {
00548   symbol_table::print_info (os, local_scope);
00549 }
00550 #endif
00551 
00552 void
00553 octave_user_function::print_code_function_header (void)
00554 {
00555   tree_print_code tpc (octave_stdout, VPS4);
00556 
00557   tpc.visit_octave_user_function_header (*this);
00558 }
00559 
00560 void
00561 octave_user_function::print_code_function_trailer (void)
00562 {
00563   tree_print_code tpc (octave_stdout, VPS4);
00564 
00565   tpc.visit_octave_user_function_trailer (*this);
00566 }
00567 
00568 void
00569 octave_user_function::bind_automatic_vars
00570   (const string_vector& arg_names, int nargin, int nargout,
00571    const octave_value_list& va_args, const std::list<octave_lvalue> *lvalue_list)
00572 {
00573   if (! arg_names.empty ())
00574     {
00575       // It is better to save this in the hidden variable .argn. and
00576       // then use that in the inputname function instead of using argn,
00577       // which might be redefined in a function.  Keep the old argn name
00578       // for backward compatibility of functions that use it directly.
00579 
00580       symbol_table::varref ("argn") = arg_names;
00581       symbol_table::varref (".argn.") = Cell (arg_names);
00582 
00583       symbol_table::mark_hidden (".argn.");
00584 
00585       symbol_table::mark_automatic ("argn");
00586       symbol_table::mark_automatic (".argn.");
00587     }
00588 
00589   symbol_table::varref (".nargin.") = nargin;
00590   symbol_table::varref (".nargout.") = nargout;
00591 
00592   symbol_table::mark_hidden (".nargin.");
00593   symbol_table::mark_hidden (".nargout.");
00594 
00595   symbol_table::mark_automatic (".nargin.");
00596   symbol_table::mark_automatic (".nargout.");
00597 
00598   if (takes_varargs ())
00599     symbol_table::varref ("varargin") = va_args.cell_value ();
00600 
00601   // Force .ignored. variable to be undefined by default.
00602   symbol_table::varref (".ignored.") = octave_value ();
00603 
00604   if (lvalue_list)
00605     {
00606       octave_idx_type nbh = 0;
00607       for (std::list<octave_lvalue>::const_iterator p = lvalue_list->begin ();
00608            p != lvalue_list->end (); p++)
00609         nbh += p->is_black_hole ();
00610 
00611       if (nbh > 0)
00612         {
00613           // Only assign the hidden variable if black holes actually present.
00614           Matrix bh (1, nbh);
00615           octave_idx_type k = 0, l = 0;
00616           for (std::list<octave_lvalue>::const_iterator p = lvalue_list->begin ();
00617                p != lvalue_list->end (); p++)
00618             {
00619               if (p->is_black_hole ())
00620                 bh(l++) = k+1;
00621               k += p->numel ();
00622             }
00623 
00624           symbol_table::varref (".ignored.") = bh;
00625         }
00626     }
00627 
00628   symbol_table::mark_hidden (".ignored.");
00629   symbol_table::mark_automatic (".ignored.");
00630 }
00631 
00632 DEFUN (nargin, args, ,
00633   "-*- texinfo -*-\n\
00634 @deftypefn  {Built-in Function} {} nargin ()\n\
00635 @deftypefnx {Built-in Function} {} nargin (@var{fcn_name})\n\
00636 Within a function, return the number of arguments passed to the function.\n\
00637 At the top level, return the number of command line arguments passed to\n\
00638 Octave.  If called with the optional argument @var{fcn_name}, return the\n\
00639 maximum number of arguments the named function can accept, or -1 if the\n\
00640 function accepts a variable number of arguments.\n\
00641 @seealso{nargout, varargin, isargout, varargout, nthargout}\n\
00642 @end deftypefn")
00643 {
00644   octave_value retval;
00645 
00646   int nargin = args.length ();
00647 
00648   if (nargin == 1)
00649     {
00650       std::string fname = args(0).string_value ();
00651 
00652       if (! error_state)
00653         {
00654           octave_value fcn_val = symbol_table::find_function (fname);
00655 
00656           if (fcn_val.is_user_function ())
00657             {
00658               octave_user_function *fcn = fcn_val.user_function_value (true);
00659 
00660               if (fcn)
00661                 {
00662                   if (fcn->takes_varargs ())
00663                     retval = -1;
00664                   else
00665                     {
00666                       tree_parameter_list *param_list = fcn->parameter_list ();
00667 
00668                       retval = param_list ? param_list->length () : 0;
00669                     }
00670                 }
00671               else
00672                 error ("nargin: loading user-defined function failed");
00673             }
00674           else
00675             {
00676               // FIXME -- what about built-in functions or functions
00677               // defined in .oct files or .mex files?
00678 
00679               error ("nargin: FCN_NAME must be a user-defined function");
00680             }
00681         }
00682       else
00683         error ("nargin: FCN_NAME must be a string");
00684     }
00685   else if (nargin == 0)
00686     {
00687       retval = symbol_table::varval (".nargin.");
00688 
00689       if (retval.is_undefined ())
00690         retval = 0;
00691     }
00692   else
00693     print_usage ();
00694 
00695   return retval;
00696 }
00697 
00698 DEFUN (nargout, args, ,
00699   "-*- texinfo -*-\n\
00700 @deftypefn  {Built-in Function} {} nargout ()\n\
00701 @deftypefnx {Built-in Function} {} nargout (@var{fcn_name})\n\
00702 Within a function, return the number of values the caller expects to\n\
00703 receive.  If called with the optional argument @var{fcn_name}, return the\n\
00704 maximum number of values the named function can produce, or -1 if the\n\
00705 function can produce a variable number of values.\n\
00706 \n\
00707 For example,\n\
00708 \n\
00709 @example\n\
00710 f ()\n\
00711 @end example\n\
00712 \n\
00713 @noindent\n\
00714 will cause @code{nargout} to return 0 inside the function @code{f} and\n\
00715 \n\
00716 @example\n\
00717 [s, t] = f ()\n\
00718 @end example\n\
00719 \n\
00720 @noindent\n\
00721 will cause @code{nargout} to return 2 inside the function\n\
00722 @code{f}.\n\
00723 \n\
00724 At the top level, @code{nargout} is undefined.\n\
00725 @seealso{nargin, varargin, isargout, varargout, nthargout}\n\
00726 @end deftypefn")
00727 {
00728   octave_value retval;
00729 
00730   int nargin = args.length ();
00731 
00732   if (nargin == 1)
00733     {
00734       std::string fname = args(0).string_value ();
00735 
00736       if (! error_state)
00737         {
00738           octave_value fcn_val = symbol_table::find_user_function (fname);
00739 
00740           octave_user_function *fcn = fcn_val.user_function_value (true);
00741 
00742           if (fcn)
00743             {
00744               if (fcn->takes_var_return ())
00745                 retval = -1;
00746               else
00747                 {
00748                   tree_parameter_list *ret_list = fcn->return_list ();
00749 
00750                   retval = ret_list ? ret_list->length () : 0;
00751                 }
00752             }
00753           else
00754             error ("nargout: invalid function");
00755         }
00756       else
00757         error ("nargout: FCN_NAME must be a string");
00758     }
00759   else if (nargin == 0)
00760     {
00761       if (! symbol_table::at_top_level ())
00762         {
00763           retval = symbol_table::varval (".nargout.");
00764 
00765           if (retval.is_undefined ())
00766             retval = 0;
00767         }
00768       else
00769         error ("nargout: invalid call at top level");
00770     }
00771   else
00772     print_usage ();
00773 
00774   return retval;
00775 }
00776 
00777 DEFUN (optimize_subsasgn_calls, args, nargout,
00778   "-*- texinfo -*-\n\
00779 @deftypefn  {Built-in Function} {@var{val} =} optimize_subsasgn_calls ()\n\
00780 @deftypefnx {Built-in Function} {@var{old_val} =} optimize_subsasgn_calls (@var{new_val})\n\
00781 @deftypefnx {Built-in Function} {} optimize_subsasgn_calls (@var{new_val}, \"local\")\n\
00782 Query or set the internal flag for subsasgn method call optimizations.\n\
00783 If true, Octave will attempt to eliminate the redundant copying when calling\n\
00784 subsasgn method of a user-defined class.\n\
00785 \n\
00786 When called from inside a function with the \"local\" option, the variable is\n\
00787 changed locally for the function and any subroutines it calls.  The original\n\
00788 variable value is restored when exiting the function.\n\
00789 @end deftypefn")
00790 {
00791   return SET_INTERNAL_VARIABLE (optimize_subsasgn_calls);
00792 }
00793 
00794 static bool val_in_table (const Matrix& table, double val)
00795 {
00796   if (table.is_empty ())
00797     return false;
00798 
00799   octave_idx_type i = table.lookup (val, ASCENDING);
00800   return (i > 0 && table(i-1) == val);
00801 }
00802 
00803 static bool isargout1 (int nargout, const Matrix& ignored, double k)
00804 {
00805   if (k != xround (k) || k <= 0)
00806     {
00807       error ("isargout: K must be a positive integer");
00808       return false;
00809     }
00810   else
00811     return (k == 1 || k <= nargout) && ! val_in_table (ignored, k);
00812 }
00813 
00814 DEFUN (isargout, args, ,
00815   "-*- texinfo -*-\n\
00816 @deftypefn {Built-in Function} {} isargout (@var{k})\n\
00817 Within a function, return a logical value indicating whether the argument\n\
00818 @var{k} will be assigned on output to a variable.  If the result is false,\n\
00819 the argument has been ignored during the function call through the use of\n\
00820 the tilde (~) special output argument.  Functions can use @code{isargout} to\n\
00821 avoid performing unnecessary calculations for outputs which are unwanted.\n\
00822 \n\
00823 If @var{k} is outside the range @code{1:max(nargout)}, the function returns\n\
00824 false.  @var{k} can also be an array, in which case the function works\n\
00825 element-by-element and a logical array is returned.  At the top level,\n\
00826 @code{isargout} returns an error.\n\
00827 @seealso{nargout, nargin, varargin, varargout, nthargout}\n\
00828 @end deftypefn")
00829 {
00830   octave_value retval;
00831 
00832   int nargin = args.length ();
00833 
00834   if (nargin == 1)
00835     {
00836       if (! symbol_table::at_top_level ())
00837         {
00838           int nargout1 = symbol_table::varval (".nargout.").int_value ();
00839           if (error_state)
00840             {
00841               error ("isargout: internal error");
00842               return retval;
00843             }
00844 
00845           Matrix ignored;
00846           octave_value tmp = symbol_table::varval (".ignored.");
00847           if (tmp.is_defined ())
00848             ignored = tmp.matrix_value ();
00849 
00850           if (args(0).is_scalar_type ())
00851             {
00852               double k = args(0).double_value ();
00853               if (! error_state)
00854                 retval = isargout1 (nargout1, ignored, k);
00855             }
00856           else if (args(0).is_numeric_type ())
00857             {
00858               const NDArray ka = args(0).array_value ();
00859               if (! error_state)
00860                 {
00861                   boolNDArray r (ka.dims ());
00862                   for (octave_idx_type i = 0; i < ka.numel () && ! error_state; i++)
00863                     r(i) = isargout1 (nargout1, ignored, ka(i));
00864 
00865                   retval = r;
00866                 }
00867             }
00868           else
00869             gripe_wrong_type_arg ("isargout", args(0));
00870         }
00871       else
00872         error ("isargout: invalid call at top level");
00873     }
00874   else
00875     print_usage ();
00876 
00877   return retval;
00878 }
00879 
00880 /*
00881 %!function [x, y] = try_isargout ()
00882 %!  if (isargout (1))
00883 %!    if (isargout (2))
00884 %!      x = 1; y = 2;
00885 %!    else
00886 %!      x = -1;
00887 %!    endif
00888 %!  else
00889 %!    if (isargout (2))
00890 %!      y = -2;
00891 %!    else
00892 %!      error ("no outputs requested");
00893 %!    endif
00894 %!  endif
00895 %!endfunction
00896 %!
00897 %!test
00898 %! [x, y] = try_isargout ();
00899 %! assert ([x, y], [1, 2]);
00900 %!
00901 %!test
00902 %! [x, ~] = try_isargout ();
00903 %! assert (x, -1);
00904 %!
00905 %!test
00906 %! [~, y] = try_isargout ();
00907 %! assert (y, -2);
00908 %!
00909 %!error [~, ~] = try_isargout ();
00910 %!
00911 %% Check to see that isargout isn't sticky:
00912 %!test
00913 %! [x, y] = try_isargout ();
00914 %! assert ([x, y], [1, 2]);
00915 */
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines