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