GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ov-usr-fcn.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2025 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <iostream>
31
32#include <sstream>
33
34#include "file-info.h"
35#include "file-ops.h"
36#include "file-stat.h"
37#include "str-vec.h"
38
39#include "builtin-defun-decls.h"
40#include "defaults.h"
41#include "Cell.h"
42#include "defun.h"
43#include "error.h"
44#include "errwarn.h"
45#include "filepos.h"
46#include "input.h"
47#include "ovl.h"
48#include "ov-usr-fcn.h"
49#include "ov.h"
50#include "pager.h"
51#include "pt-cmd.h"
52#include "pt-eval.h"
53#include "pt-id.h"
54#include "pt-jump.h"
55#include "pt-misc.h"
56#include "pt-pr-code.h"
57#include "pt-stmt.h"
58#include "pt-walk.h"
59#include "symtab.h"
60#include "interpreter-private.h"
61#include "interpreter.h"
62#include "unwind-prot.h"
63#include "utils.h"
64#include "parse.h"
65#include "profiler.h"
66#include "variables.h"
67#include "ov-fcn-handle.h"
68
69// Whether to optimize subsasgn method calls.
70static bool Voptimize_subsasgn_calls = true;
71
73{
74 // This function is no longer valid, so remove the pointer to it from
75 // the corresponding scope.
76 // FIXME: would it be better to use shared/weak pointers for this job
77 // instead of storing a bare pointer in the scope object?
78 m_scope.set_user_code (nullptr);
79
80 // FIXME: shouldn't this happen automatically when deleting cmd_list?
81 if (m_cmd_list)
82 {
83 octave::event_manager& evmgr = octave::__get_event_manager__ ();
84
85 m_cmd_list->remove_all_breakpoints (evmgr, m_file_name);
86 }
87
88 delete m_cmd_list;
89 delete m_file_info;
90}
91
92octave::filepos octave_user_code::beg_pos () const
93{
94 return m_cmd_list->beg_pos ();
95}
96
97octave::filepos octave_user_code::end_pos () const
98{
99 return m_cmd_list->end_pos ();
100}
101
102void
104{
105 m_file_info = new octave::file_info (m_file_name);
106
107 octave::sys::file_stat fs (m_file_name);
108
109 if (fs && (fs.mtime () > time_parsed ()))
110 warning ("function file '%s' changed since it was parsed",
111 m_file_name.c_str ());
112}
113
114std::string
116{
117 if (! m_file_info)
118 get_file_info ();
119
120 return m_file_info->get_line (line);
121}
122
123std::deque<std::string>
124octave_user_code::get_code_lines (std::size_t line, std::size_t num_lines)
125{
126 if (! m_file_info)
127 get_file_info ();
128
129 return m_file_info->get_lines (line, num_lines);
130}
131
132void
134 const octave::sys::time& timestamp)
135{
136 delete m_file_info;
137
138 if (timestamp > time_parsed ())
139 warning ("help text for function is newer than function");
140
141 m_file_info = new octave::file_info (text, timestamp);
142}
143
144std::map<std::string, octave_value>
146{
147 return std::map<std::string, octave_value> ();
148}
149
152{
153 std::map<std::string, octave_value> m
154 = {{ "scope_info", m_scope ? m_scope.dump () : "0x0" },
155 { "m_file_name", m_file_name },
156 { "time_parsed", m_t_parsed },
157 { "time_checked", m_t_checked }
158 };
159
160 return octave_value (m);
161}
162
163
164// User defined scripts.
165
167 "user-defined script",
168 "user-defined script");
169
173
175(const std::string& fnm, const std::string& nm,
176 const octave::symbol_scope& scope, octave::tree_statement_list *cmds,
177 const std::string& ds)
178 : octave_user_code (fnm, nm, scope, cmds, ds)
179{
180 if (m_cmd_list)
181 m_cmd_list->mark_as_script_body ();
182}
183
185(const std::string& fnm, const std::string& nm,
186 const octave::symbol_scope& scope, const std::string& ds)
187 : octave_user_code (fnm, nm, scope, nullptr, ds)
188{ }
189
190// We must overload the call method so that we call the proper
191// push_stack_frame method, which is overloaded for pointers to
192// octave_function, octave_user_function, and octave_user_script
193// objects.
194
196octave_user_script::call (octave::tree_evaluator& tw, int nargout,
197 const octave_value_list& args)
198{
199 tw.push_stack_frame (this);
200
201 octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
202
203 return execute (tw, nargout, args);
204}
205
207octave_user_script::execute (octave::tree_evaluator& tw, int nargout,
208 const octave_value_list& args)
209{
210 return tw.execute_user_script (*this, nargout, args);
211}
212
213void
214octave_user_script::accept (octave::tree_walker& tw)
215{
216 tw.visit_octave_user_script (*this);
217}
218
219// User defined functions.
220
222 "user-defined function",
223 "user-defined function");
224
225// Ugh. This really needs to be simplified (code/data?
226// extrinsic/intrinsic state?).
227
228octave_user_function::octave_user_function (const octave::symbol_scope& scope, octave::tree_identifier *id, octave::tree_parameter_list *pl, octave::tree_parameter_list *rl, octave::tree_statement_list *cl)
229 : octave_user_code ("", "", scope, cl, ""), m_id (id), m_param_list (pl), m_ret_list (rl), m_num_named_args (m_param_list ? m_param_list->size () : 0)
230{
231 if (m_cmd_list)
232 m_cmd_list->mark_as_function_body ();
233}
234
236{
237 delete m_id;
238 delete m_param_list;
239 delete m_ret_list;
240}
241
242std::string
244{
245 std::ostringstream buf;
246
247 octave::tree_print_code tpc (buf);
248
249 if (m_ret_list)
250 {
251 m_ret_list->accept (tpc);
252 buf << " = ";
253 }
254
255 buf << m_name << " ";
256
257 if (m_param_list)
258 m_param_list->accept (tpc);
259 else
260 buf << " ()";
261
262 return buf.str ();
263}
264
266octave_user_function::define_ret_list (octave::tree_parameter_list *t)
267{
268 m_ret_list = t;
269
270 return this;
271}
272
273void
274octave_user_function::attach_trailing_comments (const octave::comment_list& lst)
275{
276 if (m_cmd_list && ! m_cmd_list->empty ())
277 {
278 octave::tree_statement *last_stmt = m_cmd_list->back ();
279
280 octave::tree_command *cmd = last_stmt->command ();
281
282 octave::tree_no_op_command *no_op_cmd = dynamic_cast <octave::tree_no_op_command *> (cmd);
283
284 if (no_op_cmd && (no_op_cmd->is_end_of_fcn_or_script () || no_op_cmd->is_end_of_file ()))
285 no_op_cmd->attach_trailing_comments (lst);
286 }
287}
288
289octave::comment_list octave_user_function::trailing_comments () const
290{
291 if (m_cmd_list && ! m_cmd_list->empty ())
292 {
293 octave::tree_statement *last_stmt = m_cmd_list->back ();
294
295 octave::tree_command *cmd = last_stmt->command ();
296
297 octave::tree_no_op_command *no_op_cmd = dynamic_cast <octave::tree_no_op_command *> (cmd);
298
299 if (no_op_cmd && (no_op_cmd->is_end_of_fcn_or_script () || no_op_cmd->is_end_of_file ()))
300 return no_op_cmd->trailing_comments ();
301 }
302
303 return octave::comment_list ();
304}
305
306// If there is no explicit end statement at the end of the function,
307// relocate the no_op that was generated for the end of file condition
308// to appear on the next line after the last statement in the file, or
309// the next line after the function keyword if there are no statements.
310
311void
312octave_user_function::maybe_relocate_end_internal ()
313{
314 // This shouldn't happen, but check and return early anyway.
315 if (m_anonymous_function)
316 return;
317
318 if (m_cmd_list && ! m_cmd_list->empty ())
319 {
320 octave::tree_statement *last_stmt = m_cmd_list->back ();
321
322 if (last_stmt && last_stmt->is_end_of_fcn_or_script ()
323 && last_stmt->is_end_of_file ())
324 {
325 octave::tree_statement_list::reverse_iterator next_to_last_elt = m_cmd_list->rbegin ();
326 next_to_last_elt++;
327
328 octave::filepos new_eof_pos;
329
330 if (next_to_last_elt == m_cmd_list->rend ())
331 {
332 // Empty function body, just the end statement. Set the
333 // new beginning of that statement to the end of the
334 // argument list (if any) or the end of the function name.
335
336 // M_ID is only nullptr if this is an anonymous function
337 // and we shouldn't be updating the end position for that.
338 // So if there is no name and no parameter list, just
339 // return early.
340
341 if (m_param_list)
342 new_eof_pos = m_param_list->end_pos ();
343 else if (m_id)
344 new_eof_pos = m_id->end_pos ();
345 else
346 return;
347 }
348 else
349 {
350 octave::tree_statement *next_to_last_stmt = *next_to_last_elt;
351
352 new_eof_pos = next_to_last_stmt->end_pos ();
353 }
354
355 last_stmt->update_end_pos (new_eof_pos);
356 }
357 }
358}
359
360void
362{
363 std::map<std::string, octave_value> fcns = subfunctions ();
364
365 if (! fcns.empty ())
366 {
367 for (auto& nm_fnval : fcns)
368 {
369 octave_user_function *f = nm_fnval.second.user_function_value ();
370
371 if (f)
372 f->maybe_relocate_end_internal ();
373 }
374 }
375
376 maybe_relocate_end_internal ();
377}
378
379void
380octave_user_function::stash_parent_fcn_scope (const octave::symbol_scope& ps)
381{
382 m_scope.set_parent (ps);
383}
384
385std::string
387{
388 std::ostringstream result;
389
390 octave::filepos bp = beg_pos ();
391
392 int bp_line = bp.line ();
393 int bp_column = bp.column ();
394
396 result << "anonymous@" << fcn_file_name ()
397 << ':' << bp_line << ':' << bp_column;
398 else if (is_subfunction ())
399 result << parent_fcn_name () << '>' << name ();
400 else if (is_class_method ())
401 result << '@' << dispatch_class () << '/' << name ();
403 result << '@' << name ();
404 else if (is_inline_function ())
405 result << "inline@" << fcn_file_name ()
406 << ':' << bp_line << ':' << bp_column;
407 else
408 result << name ();
409
410 return result.str ();
411}
412
413void
415{
416 if (! m_file_name.empty ())
417 {
418 // We really should stash the whole path to the file we found,
419 // when we looked it up, to avoid possible race conditions...
420 // FIXME
421 //
422 // We probably also don't need to get the library directory
423 // every time, but since this function is only called when the
424 // function file is parsed, it probably doesn't matter that
425 // much.
426
427 std::string ff_name = octave::fcn_file_in_path (m_file_name);
428
429 static const std::string canonical_fcn_file_dir
430 = octave::sys::canonicalize_file_name
431 (octave::config::fcn_file_dir ());
432 static const std::string fcn_file_dir
433 = canonical_fcn_file_dir.empty () ? octave::config::fcn_file_dir ()
434 : canonical_fcn_file_dir;
435
436 if (fcn_file_dir == ff_name.substr (0, fcn_file_dir.length ()))
437 m_system_fcn_file = true;
438 }
439 else
440 m_system_fcn_file = false;
441}
442
443void
445{
446 m_scope.erase_subfunctions ();
447}
448
449bool
451{
452 return (m_param_list && m_param_list->takes_varargs ());
453}
454
455bool
457{
458 return (m_ret_list && m_ret_list->takes_varargs ());
459}
460
461void
463{
464 m_scope.mark_subfunctions_in_scope_as_private (cname);
465
467}
468
469void
471{
472 m_scope.lock_subfunctions ();
473}
474
475void
477{
478 m_scope.unlock_subfunctions ();
479}
480
481std::map<std::string, octave_value>
483{
484 return m_scope.subfunctions ();
485}
486
487// Find definition of final subfunction in list of subfcns:
488//
489// sub1>sub2>...>subN
490
492octave_user_function::find_subfunction (const std::string& subfcns_arg) const
493{
494 std::string subfcns = subfcns_arg;
495
496 std::string first_fcn = subfcns;
497
498 std::size_t pos = subfcns.find ('>');
499
500 if (pos == std::string::npos)
501 subfcns = "";
502 else
503 {
504 first_fcn = subfcns.substr (0, pos-1);
505 subfcns = subfcns.substr (pos+1);
506 }
507
508 octave_value ov_fcn = m_scope.find_subfunction (first_fcn);
509
510 if (subfcns.empty ())
511 return ov_fcn;
512
514
515 return fcn->find_subfunction (subfcns);
516}
517
518bool
520{
521 return m_scope.has_subfunctions ();
522}
523
524void
525octave_user_function::stash_subfunction_names (const std::list<std::string>& names)
526{
527 m_scope.stash_subfunction_names (names);
528}
529
530std::list<std::string>
532{
533 return m_scope.subfunction_names ();
534}
535
538{
539 octave_value_list retval;
540
541 octave_idx_type n = args.length () - m_num_named_args;
542
543 if (n > 0)
544 retval = args.slice (m_num_named_args, n);
545
546 return retval;
547}
548
549// We must overload the call method so that we call the proper
550// push_stack_frame method, which is overloaded for pointers to
551// octave_function, octave_user_function, and octave_user_script
552// objects.
553
555octave_user_function::call (octave::tree_evaluator& tw, int nargout,
556 const octave_value_list& args)
557{
558 tw.push_stack_frame (this);
559
560 octave::unwind_action act ([&tw] () { tw.pop_stack_frame (); });
561
562 return execute (tw, nargout, args);
563}
564
566octave_user_function::execute (octave::tree_evaluator& tw, int nargout,
567 const octave_value_list& args)
568{
569 return tw.execute_user_function (*this, nargout, args);
570}
571
572void
573octave_user_function::accept (octave::tree_walker& tw)
574{
575 tw.visit_octave_user_function (*this);
576}
577
578octave::tree_expression *
580{
582 panic_if (m_cmd_list->size () != 1);
583
584 octave::tree_statement *stmt = m_cmd_list->front ();
585 return stmt->expression ();
586}
587
588bool
590{
591 bool retval = false;
592 if (Voptimize_subsasgn_calls
593 && m_param_list && m_ret_list
594 && m_param_list->size () > 0 && ! m_param_list->varargs_only ()
595 && m_ret_list->size () == 1 && ! m_ret_list->takes_varargs ())
596 {
597 octave::tree_identifier *par1 = m_param_list->front ()->ident ();
598 octave::tree_identifier *ret1 = m_ret_list->front ()->ident ();
599 retval = par1->name () == ret1->name ();
600 }
601
602 return retval;
603}
604
605std::string
606octave_user_function::ctor_type_str () const
607{
608 std::string retval;
609
610 switch (m_class_constructor)
611 {
612 case none:
613 retval = "none";
614 break;
615
616 case legacy:
617 retval = "legacy";
618 break;
619
620 case classdef:
621 retval = "classdef";
622 break;
623
624 default:
625 retval = "unrecognized enum value";
626 break;
627 }
628
629 return retval;
630}
631
632std::string
633octave_user_function::method_type_str () const
634{
635 std::string retval;
636
637 switch (m_class_method)
638 {
639 case none:
640 retval = "none";
641 break;
642
643 case legacy:
644 retval = "legacy";
645 break;
646
647 case classdef:
648 retval = "classdef";
649 break;
650
651 default:
652 retval = "unrecognized enum value";
653 break;
654 }
655
656 return retval;
657}
658
661{
662 octave::filepos bp = beg_pos ();
663 octave::filepos ep = end_pos ();
664
665 std::map<std::string, octave_value> m
666 = {{ "user_code", octave_user_code::dump () },
667 { "line", bp.line () },
668 { "col", bp.column () },
669 { "end_line", ep.line () },
670 { "end_col", ep.column () },
671 { "system_fcn_file", m_system_fcn_file },
672 { "num_named_args", m_num_named_args },
673 { "subfunction", m_subfunction },
674 { "inline_function", m_inline_function },
675 { "anonymous_function", m_anonymous_function },
676 { "nested_function", m_nested_function },
677 { "ctor_type", ctor_type_str () },
678 { "class_method", m_class_method }
679 };
680
681 return octave_value (m);
682}
683
684void
685octave_user_function::print_code_function_header (const std::string& prefix)
686{
687 octave::tree_print_code tpc (octave_stdout, prefix);
688
689 tpc.visit_octave_user_function_header (*this);
690}
691
692void
693octave_user_function::print_code_function_trailer (const std::string& prefix)
694{
695 octave::tree_print_code tpc (octave_stdout, prefix);
696
697 tpc.visit_octave_user_function_trailer (*this);
698}
699
700void
702{
703 octave::interpreter& interp = octave::__get_interpreter__ ();
704
705 octave::tree_evaluator& tw = interp.get_evaluator ();
706
707 octave_value val
708 = tw.get_auto_fcn_var (octave::stack_frame::SAVED_WARNING_STATES);
709
710 if (val.is_defined ())
711 {
712 // Fail if SAVED_WARNING_STATES is not an octave_map (or
713 // octave_scalar_map) object.
714
715 if (! val.isstruct ())
716 error ("unexpected: SAVED_WARNING_STATES is not a struct in octave_user_function::restore_warning_states - please report this bug");
717
718 octave_map m = val.map_value ();
719
720 Cell ids = m.contents ("identifier");
721 Cell states = m.contents ("state");
722
723 for (octave_idx_type i = 0; i < m.numel (); i++)
724 Fwarning (interp, ovl (states(i), ids(i)));
725 }
726}
727
729
730DEFMETHOD (nargin, interp, args, ,
731 doc: /* -*- texinfo -*-
732@deftypefn {} {@var{n} =} nargin ()
733@deftypefnx {} {@var{n} =} nargin (@var{fcn})
734Report the number of input arguments to a function.
735
736Called from within a function, return the number of arguments passed to the
737function. At the top level, return the number of command line arguments
738passed to Octave.
739
740If called with the optional argument @var{fcn}---a function name or
741handle---return the declared number of arguments that the function can
742accept.
743
744If the last argument to @var{fcn} is @var{varargin} the returned value is
745negative. For example, the function @code{union} for sets is declared as
746
747@example
748@group
749function [y, ia, ib] = union (a, b, varargin)
750
751and
752
753nargin ("union")
754@result{} -3
755@end group
756@end example
757
758Programming Note: @code{nargin} does not work on compiled functions
759(@file{.oct} files) such as built-in or dynamically loaded functions.
760@seealso{nargout, narginchk, varargin, inputname}
761@end deftypefn */)
762{
763 int nargin = args.length ();
764
765 if (nargin > 1)
766 print_usage ();
767
768 octave_value retval;
769
770 if (nargin == 1)
771 {
772 octave_value fcn = args(0);
773
774 if (fcn.is_string ())
775 {
776 symbol_table& symtab = interp.get_symbol_table ();
777
778 std::string name = fcn.string_value ();
779 fcn = symtab.find_function (name);
780 if (fcn.is_undefined ())
781 error ("nargin: invalid function name: %s", name.c_str ());
782 }
783
784 octave_function *fcn_val = fcn.function_value (true);
785 if (! fcn_val)
786 error ("nargin: FCN must be a string or function handle");
787
788 octave_user_function *ufcn = fcn_val->user_function_value (true);
789
790 if (! ufcn)
791 {
792 // Matlab gives up for histc, so maybe it's ok that we
793 // give up sometimes too?
794
795 std::string type = fcn_val->type_name ();
796 error ("nargin: number of input arguments unavailable for %s objects",
797 type.c_str ());
798 }
799
800 tree_parameter_list *m_param_list = ufcn->parameter_list ();
801
802 retval = (m_param_list ? m_param_list->size () : 0);
803 if (ufcn->takes_varargs ())
804 retval = -1 - retval;
805 }
806 else
807 {
808 tree_evaluator& tw = interp.get_evaluator ();
809
811
812 if (retval.is_undefined ())
813 retval = 0;
814 }
815
816 return retval;
817}
818
819DEFMETHOD (nargout, interp, args, ,
820 doc: /* -*- texinfo -*-
821@deftypefn {} {@var{n} =} nargout ()
822@deftypefnx {} {@var{n} =} nargout (@var{fcn})
823Report the number of output arguments from a function.
824
825Called from within a function, return the number of values the caller
826expects to receive. At the top level, @code{nargout} with no argument is
827undefined and will produce an error.
828
829If called with the optional argument @var{fcn}---a function name or
830handle---return the number of declared output values that the function can
831produce.
832
833If the final output argument is @var{varargout} the returned value is
834negative.
835
836For example,
837
838@example
839f ()
840@end example
841
842@noindent
843will cause @code{nargout} to return 0 inside the function @code{f} and
844
845@example
846[s, t] = f ()
847@end example
848
849@noindent
850will cause @code{nargout} to return 2 inside the function @code{f}.
851
852In the second usage,
853
854@example
855nargout (@@histc) # or nargout ("histc") using a string input
856@end example
857
858@noindent
859will return 2, because @code{histc} has two outputs, whereas
860
861@example
862nargout (@@imread)
863@end example
864
865@noindent
866will return -2, because @code{imread} has two outputs and the second is
867@var{varargout}.
868
869Programming Note. @code{nargout} does not work for built-in functions and
870returns -1 for all anonymous functions.
871@seealso{nargin, varargout, isargout, nthargout}
872@end deftypefn */)
873{
874 int nargin = args.length ();
875
876 if (nargin > 1)
877 print_usage ();
878
879 octave_value retval;
880
881 if (nargin == 1)
882 {
883 octave_value fcn = args(0);
884
885 if (fcn.is_string ())
886 {
887 symbol_table& symtab = interp.get_symbol_table ();
888
889 std::string name = fcn.string_value ();
890 fcn = symtab.find_function (name);
891 if (fcn.is_undefined ())
892 error ("nargout: invalid function name: %s", name.c_str ());
893 }
894
895 if (fcn.is_inline_function ())
896 return ovl (1);
897
898 if (fcn.is_function_handle ())
899 {
901
902 if (fh->is_anonymous ())
903 return ovl (-1);
904 }
905
906 octave_function *fcn_val = fcn.function_value (true);
907 if (! fcn_val)
908 error ("nargout: FCN must be a string or function handle");
909
910 octave_user_function *ufcn = fcn_val->user_function_value (true);
911
912 if (! ufcn)
913 {
914 // Matlab gives up for histc, so maybe it's ok that we
915 // give up sometimes too?
916
917 std::string type = fcn_val->type_name ();
918 error ("nargout: number of output arguments unavailable for %s objects",
919 type.c_str ());
920 }
921
922 tree_parameter_list *m_ret_list = ufcn->return_list ();
923
924 retval = (m_ret_list ? m_ret_list->size () : 0);
925
926 if (ufcn->takes_var_return ())
927 retval = -1 - retval;
928 }
929 else
930 {
931 if (interp.at_top_level ())
932 error ("nargout: invalid call at top level");
933
934 tree_evaluator& tw = interp.get_evaluator ();
935
937
938 if (retval.is_undefined ())
939 retval = 0;
940 }
941
942 return retval;
943}
944
945DEFUN (optimize_subsasgn_calls, args, nargout,
946 doc: /* -*- texinfo -*-
947@deftypefn {} {@var{val} =} optimize_subsasgn_calls ()
948@deftypefnx {} {@var{old_val} =} optimize_subsasgn_calls (@var{new_val})
949@deftypefnx {} {@var{old_val} =} optimize_subsasgn_calls (@var{new_val}, "local")
950Query or set the internal flag for @code{subsasgn} method call
951optimizations.
952
953If true, Octave will attempt to eliminate the redundant copying when calling
954the @code{subsasgn} method of a user-defined class.
955
956When called from inside a function with the @qcode{"local"} option, the
957variable is changed locally for the function and any subroutines it calls.
958The original variable value is restored when exiting the function.
959@seealso{subsasgn}
960@end deftypefn */)
961{
962 return set_internal_variable (Voptimize_subsasgn_calls, args, nargout,
963 "optimize_subsasgn_calls");
964}
965
966static bool
967val_in_table (const Matrix& table, double val)
968{
969 if (table.isempty ())
970 return false;
971
972 octave_idx_type i = table.lookup (val, ASCENDING);
973 return (i > 0 && table(i-1) == val);
974}
975
976static bool
977isargout1 (int nargout, const Matrix& ignored, double k)
978{
979 if (k != math::fix (k) || k <= 0)
980 error ("isargout: K must be a positive integer");
981
982 return (k == 1 || k <= nargout) && ! val_in_table (ignored, k);
983}
984
985DEFMETHOD (isargout, interp, args, ,
986 doc: /* -*- texinfo -*-
987@deftypefn {} {@var{tf} =} isargout (@var{k})
988Within a function, return a logical value indicating whether the argument
989@var{k} will be assigned to a variable on output.
990
991If the result is false, the argument has been ignored during the function
992call through the use of the tilde (~) special output argument. Functions
993can use @code{isargout} to avoid performing unnecessary calculations for
994outputs which are unwanted.
995
996If @var{k} is outside the range @code{1:max (nargout)}, the function returns
997false. @var{k} can also be an array, in which case the function works
998element-by-element and a logical array is returned. At the top level,
999@code{isargout} returns an error.
1000@seealso{nargout, varargout, nthargout}
1001@end deftypefn */)
1002{
1003 if (args.length () != 1)
1004 print_usage ();
1005
1006 if (interp.at_top_level ())
1007 error ("isargout: invalid call at top level");
1008
1009 tree_evaluator& tw = interp.get_evaluator ();
1010
1011 octave_value tmp;
1012
1013 int nargout1 = 0;
1015 if (tmp.is_defined ())
1016 nargout1 = tmp.int_value ();
1017
1018 Matrix ignored;
1020 if (tmp.is_defined ())
1021 ignored = tmp.matrix_value ();
1022
1023 if (args(0).is_scalar_type ())
1024 {
1025 double k = args(0).double_value ();
1026
1027 return ovl (isargout1 (nargout1, ignored, k));
1028 }
1029 else if (args(0).isnumeric ())
1030 {
1031 const NDArray ka = args(0).array_value ();
1032
1033 boolNDArray r (ka.dims ());
1034 for (octave_idx_type i = 0; i < ka.numel (); i++)
1035 r(i) = isargout1 (nargout1, ignored, ka(i));
1036
1037 return ovl (r);
1038 }
1039 else
1040 err_wrong_type_arg ("isargout", args(0));
1041
1042 return ovl ();
1043}
1044
1045/*
1046%!function [x, y] = try_isargout ()
1047%! if (isargout (1))
1048%! if (isargout (2))
1049%! x = 1; y = 2;
1050%! else
1051%! x = -1;
1052%! endif
1053%! else
1054%! if (isargout (2))
1055%! y = -2;
1056%! else
1057%! error ("no outputs requested");
1058%! endif
1059%! endif
1060%!endfunction
1061%!
1062%!function [a, b] = try_isargout2 (x, y)
1063%! a = y;
1064%! b = {isargout(1), isargout(2), x};
1065%!endfunction
1066%!
1067%!test
1068%! [x, y] = try_isargout ();
1069%! assert ([x, y], [1, 2]);
1070%!
1071%!test
1072%! [x, ~] = try_isargout ();
1073%! assert (x, -1);
1074%!
1075%!test
1076%! [~, y] = try_isargout ();
1077%! assert (y, -2);
1078%!
1079%!error [~, ~] = try_isargout ()
1080%!
1081## Check to see that isargout isn't sticky:
1082%!test
1083%! [x, y] = try_isargout ();
1084%! assert ([x, y], [1, 2]);
1085%!
1086## It should work without ():
1087%!test
1088%! [~, y] = try_isargout;
1089%! assert (y, -2);
1090%!
1091## It should work in function handles,
1092%!test
1093%! fh = @try_isargout;
1094%! [~, y] = fh ();
1095%! assert (y, -2);
1096%!
1097## anonymous functions,
1098%!test
1099%! af = @() try_isargout;
1100%! [~, y] = af ();
1101%! assert (y, -2);
1102%!
1103## and cell arrays of handles or anonymous functions.
1104%!test
1105%! fh = @try_isargout;
1106%! af = @() try_isargout;
1107%! c = {fh, af};
1108%! [~, y] = c{1}();
1109%! assert (y, -2);
1110%!test
1111%! fh = @try_isargout;
1112%! af = @() try_isargout;
1113%! c = {fh, af};
1114%! [~, y] = c{2}();
1115%! assert (y, -2);
1116%!
1117## Nesting, anyone?
1118%!test
1119%! [~, b] = try_isargout2 (try_isargout, rand);
1120%! assert (b, {0, 1, -1});
1121%!test
1122%! [~, b] = try_isargout2 ({try_isargout, try_isargout}, rand);
1123%! assert (b, {0, 1, {-1, -1}});
1124*/
1125
1126OCTAVE_END_NAMESPACE(octave)
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array.h:507
bool isempty() const
Size of the specified dimension.
Definition Array.h:652
octave_idx_type lookup(const T &value, sortmode mode=UNSORTED) const
Do a binary lookup in a sorted array.
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
virtual octave_user_function * user_function_value(bool silent=false)
Definition ov-base.cc:944
virtual std::string type_name() const
Definition ov-base.h:977
friend class octave_value
Definition ov-base.h:278
bool is_anonymous() const
std::string dispatch_class() const
Definition ov-fcn.h:150
bool is_class_method(const std::string &cname="") const
Definition ov-fcn.h:117
bool is_class_constructor(const std::string &cname="") const
Definition ov-fcn.h:112
std::string m_name
Definition ov-fcn.h:267
virtual void mark_as_private_function(const std::string &cname="")
Definition ov-fcn.h:157
std::string name() const
Definition ov-fcn.h:208
const Cell & contents(const_iterator p) const
Definition oct-map.h:310
octave_idx_type numel() const
Definition oct-map.h:368
octave::filepos end_pos() const
Definition ov-usr-fcn.cc:97
octave::filepos beg_pos() const
Definition ov-usr-fcn.cc:92
std::string get_code_line(std::size_t line)
std::string m_file_name
Definition ov-usr-fcn.h:137
octave::sys::time m_t_checked
Definition ov-usr-fcn.h:144
std::string fcn_file_name() const
Definition ov-usr-fcn.h:112
octave_value dump() const
std::deque< std::string > get_code_lines(std::size_t line, std::size_t num_lines)
virtual std::map< std::string, octave_value > subfunctions() const
octave::tree_statement_list * m_cmd_list
Definition ov-usr-fcn.h:151
octave::sys::time m_t_parsed
Definition ov-usr-fcn.h:140
octave::file_info * m_file_info
Definition ov-usr-fcn.h:148
void cache_function_text(const std::string &text, const octave::sys::time &timestamp)
octave::symbol_scope m_scope
Definition ov-usr-fcn.h:134
octave::sys::time time_parsed() const
Definition ov-usr-fcn.h:114
std::list< std::string > subfunction_names() const
octave_value_list call(octave::tree_evaluator &tw, int nargout=0, const octave_value_list &args=octave_value_list())
bool is_inline_function() const
Definition ov-usr-fcn.h:312
octave::tree_parameter_list * parameter_list()
Definition ov-usr-fcn.h:387
octave::comment_list trailing_comments() const
bool takes_varargs() const
bool is_special_expr() const
Definition ov-usr-fcn.h:331
void stash_parent_fcn_scope(const octave::symbol_scope &ps)
octave_user_function * define_ret_list(octave::tree_parameter_list *t)
std::string profiler_name() const
octave_value_list all_va_args(const octave_value_list &args)
void stash_subfunction_names(const std::list< std::string > &names)
bool is_classdef_constructor(const std::string &cname="") const
Definition ov-usr-fcn.h:352
octave_value_list execute(octave::tree_evaluator &tw, int nargout=0, const octave_value_list &args=octave_value_list())
octave::tree_expression * special_expr()
bool subsasgn_optimization_ok()
void mark_as_private_function(const std::string &cname="")
octave::tree_parameter_list * return_list()
Definition ov-usr-fcn.h:389
std::string parent_fcn_name() const
Definition ov-usr-fcn.h:255
octave_user_function * user_function_value(bool=false)
Definition ov-usr-fcn.h:230
bool is_anonymous_function() const
Definition ov-usr-fcn.h:316
void accept(octave::tree_walker &tw)
std::string signature() const
octave_value find_subfunction(const std::string &subfuns) const
bool is_subfunction() const
Definition ov-usr-fcn.h:308
bool has_subfunctions() const
std::map< std::string, octave_value > subfunctions() const
octave_user_function(const octave::symbol_scope &scope=octave::symbol_scope::anonymous(), octave::tree_identifier *id=nullptr, octave::tree_parameter_list *pl=nullptr, octave::tree_parameter_list *rl=nullptr, octave::tree_statement_list *cl=nullptr)
bool takes_var_return() const
void attach_trailing_comments(const octave::comment_list &lst)
octave::filepos beg_pos() const
Definition ov-usr-fcn.h:246
octave_value dump() const
void accept(octave::tree_walker &tw)
octave_value_list call(octave::tree_evaluator &tw, int nargout=0, const octave_value_list &args=octave_value_list())
octave_value_list execute(octave::tree_evaluator &tw, int nargout=0, const octave_value_list &args=octave_value_list())
octave_value_list slice(octave_idx_type offset, octave_idx_type len, bool tags=false) const
Definition ovl.h:129
octave_idx_type length() const
Definition ovl.h:111
bool is_function_handle() const
Definition ov.h:768
bool is_undefined() const
Definition ov.h:595
bool is_inline_function() const
Definition ov.h:774
octave_function * function_value(bool silent=false) const
int int_value(bool req_int=false, bool frc_str_conv=false) const
Definition ov.h:812
bool is_string() const
Definition ov.h:637
octave_value dump() const
Definition ov.h:1454
bool is_defined() const
Definition ov.h:592
octave_map map_value() const
std::string string_value(bool force=false) const
Definition ov.h:983
octave_fcn_handle * fcn_handle_value(bool silent=false) const
octave_idx_type length() const
octave_user_function * user_function_value(bool silent=false) const
bool isstruct() const
Definition ov.h:649
Matrix matrix_value(bool frc_str_conv=false) const
Definition ov.h:859
octave_value find_function(const std::string &name, const symbol_scope &search_scope=symbol_scope::invalid())
Definition symtab.cc:254
octave_value get_auto_fcn_var(stack_frame::auto_var_type avt) const
Definition pt-eval.cc:2241
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string fcn_file_dir()
Definition defaults.cc:345
void print_usage()
Definition defun-int.h:72
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition defun.h:111
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition defun.h:56
void warning(const char *fmt,...)
Definition error.cc:1078
octave_value_list Fwarning(octave::interpreter &interp, const octave_value_list &args, int nargout)
Definition error.cc:1492
void error(const char *fmt,...)
Definition error.cc:1003
void err_wrong_type_arg(const char *name, const char *s)
Definition errwarn.cc:166
F77_RET_T const F77_DBLE const F77_DBLE * f
@ ASCENDING
Definition oct-sort.h:97
#define DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(t, n, c)
Definition ov-base.h:246
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
#define octave_stdout
Definition pager.h:301
#define panic_if(cond)
Definition panic.h:57
#define panic_unless(cond)
Definition panic.h:59
octave_value set_internal_variable(bool &var, const octave_value_list &args, int nargout, const char *nm)
Definition variables.cc:583