GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ov-fcn-handle.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2003-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 <istream>
31#include <list>
32#include <ostream>
33#include <sstream>
34#include <string>
35#include <vector>
36
37#include "file-ops.h"
38#include "oct-locbuf.h"
39
40#include "defaults.h"
41#include "defun.h"
42#include "error.h"
43#include "errwarn.h"
44#include "file-stat.h"
45#include "input.h"
46#include "interpreter-private.h"
47#include "interpreter.h"
48#include "load-path.h"
49#include "oct-env.h"
50#include "oct-hdf5.h"
51#include "oct-map.h"
52#include "ov-base.h"
53#include "ov-cell.h"
54#include "ov-fcn-handle.h"
55#include "ov-usr-fcn.h"
56#include "parse.h"
57#include "pr-output.h"
58#include "pt-arg-list.h"
59#include "pt-assign.h"
60#include "pt-cmd.h"
61#include "pt-eval.h"
62#include "pt-exp.h"
63#include "pt-idx.h"
64#include "pt-misc.h"
65#include "pt-pr-code.h"
66#include "pt-stmt.h"
67#include "stack-frame.h"
68#include "syminfo.h"
69#include "symscope.h"
70#include "unwind-prot.h"
71#include "variables.h"
72
73#include "byte-swap.h"
74#include "ls-ascii-helper.h"
75#include "ls-hdf5.h"
76#include "ls-oct-text.h"
77#include "ls-oct-binary.h"
78#include "ls-utils.h"
79
80
82 "function handle",
83 "function_handle");
84
85const std::string octave_fcn_handle::anonymous ("@<anonymous>");
86
88
89class invalid_fcn_handle : public base_fcn_handle
90{
91public:
92
93 invalid_fcn_handle () : base_fcn_handle ("<invalid>") { }
94
95 invalid_fcn_handle (const invalid_fcn_handle&) = default;
96
97 ~invalid_fcn_handle () = default;
98
99 invalid_fcn_handle * clone () const
100 {
101 return new invalid_fcn_handle (*this);
102 }
103
104 std::string type () const { return "<invalid>"; }
105
106 octave_value_list call (int nargout, const octave_value_list& args);
107};
108
109// Create a handle to an unnamed internal function. There will be no
110// way to save and reload it. See, for example, the F__fltk_check__
111// function in __init_fltk__.cc.
112
113class internal_fcn_handle : public base_fcn_handle
114{
115public:
116
117 internal_fcn_handle (const octave_value& fcn)
118 : base_fcn_handle ("<internal>"), m_fcn (fcn)
119 { }
120
121 internal_fcn_handle (const internal_fcn_handle&) = default;
122
123 ~internal_fcn_handle () = default;
124
125 internal_fcn_handle * clone () const
126 {
127 return new internal_fcn_handle (*this);
128 }
129
130 std::string type () const { return "<internal>"; }
131
132 bool is_internal () const { return true; }
133
134 octave_value_list call (int nargout, const octave_value_list& args);
135
136 // FIXME: These must go away. They don't do the right thing for
137 // scoping or overloads.
138 octave_function * function_value (bool = false)
139 {
140 return m_fcn.function_value ();
141 }
142
144 {
145 return m_fcn.user_function_value ();
146 }
147
148 octave_value fcn_val () { return m_fcn; }
149
150 // Should be const.
152
153 friend bool is_equal_to (const internal_fcn_handle& fh1,
154 const internal_fcn_handle& fh2);
155
156private:
157
158 octave_value m_fcn;
159};
160
161class simple_fcn_handle : public base_fcn_handle
162{
163public:
164
165 // FIXME: octaveroot is temporary information used when loading
166 // handles. Can we avoid using it in the constructor?
167
168 simple_fcn_handle (const std::string& name = "",
169 const std::string& file = "",
170 const std::string& /*octaveroot*/ = "")
171 : base_fcn_handle (name, file), m_fcn ()
172 { }
173
174 simple_fcn_handle (const octave_value& fcn, const std::string& name)
175 : base_fcn_handle (name), m_fcn (fcn)
176 {
177 if (m_fcn.is_defined ())
178 {
179 octave_function *oct_fcn = m_fcn.function_value ();
180
181 if (oct_fcn)
182 m_file = oct_fcn->fcn_file_name ();
183 }
184 }
185
186 simple_fcn_handle (const simple_fcn_handle&) = default;
187
188 ~simple_fcn_handle () = default;
189
190 simple_fcn_handle * clone () const
191 {
192 return new simple_fcn_handle (*this);
193 }
194
195 std::string type () const { return "simple"; }
196
197 bool is_simple () const { return true; }
198
199 octave_value_list call (int nargout, const octave_value_list& args);
200
201 // FIXME: These must go away. They don't do the right thing for
202 // scoping or overloads.
204
206
208
209 // Should be const.
211
212 bool save_ascii (std::ostream& os);
213
214 bool load_ascii (std::istream& is);
215
216 bool save_binary (std::ostream& os, bool save_as_floats);
217
218 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
219
220 bool save_hdf5 (octave_hdf5_id loc_hid, const char *name,
221 bool save_as_floats);
222
223 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
224 octave_hdf5_id& type_hid);
225
226 void print_raw (std::ostream& os, bool pr_as_read_syntax,
227 int current_print_indent_level) const;
228
229 friend bool is_equal_to (const simple_fcn_handle& fh1,
230 const simple_fcn_handle& fh2);
231
232private:
233
234 octave_value m_fcn;
235};
236
237class scoped_fcn_handle : public base_fcn_handle
238{
239public:
240
241 // FIXME: octaveroot is temporary information used when loading
242 // handles. Can we avoid using it in the constructor?
243
244 scoped_fcn_handle (const std::string& name = "",
245 const std::string& file = "",
246 const std::string& /*octaveroot*/ = "")
247 : base_fcn_handle (name, file)
248 { }
249
250 scoped_fcn_handle (const octave_value& fcn, const std::string& name,
251 const std::list<std::string>& parentage);
252
253 scoped_fcn_handle (const scoped_fcn_handle&) = default;
254
255 ~scoped_fcn_handle () = default;
256
257 scoped_fcn_handle * clone () const
258 {
259 return new scoped_fcn_handle (*this);
260 }
261
262 std::string type () const { return "scopedfunction"; }
263
264 bool is_scoped () const { return true; }
265
266 octave_value_list call (int nargout, const octave_value_list& args);
267
268 // FIXME: These must go away. They don't do the right thing for
269 // scoping or overloads.
270 octave_function * function_value (bool = false)
271 {
272 return m_fcn.function_value ();
273 }
274
276 {
277 return m_fcn.user_function_value ();
278 }
279
280 octave_value fcn_val () { return m_fcn; }
281
282 // Should be const.
284
285 bool save_ascii (std::ostream& os);
286
287 bool load_ascii (std::istream& is);
288
289 bool save_binary (std::ostream& os, bool save_as_floats);
290
291 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
292
293 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
294 bool save_as_floats);
295
296 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
297 octave_hdf5_id& type_hid);
298
299 void print_raw (std::ostream&, bool pr_as_read_syntax,
300 int current_print_indent_level) const;
301
302 friend bool is_equal_to (const scoped_fcn_handle& fh1,
303 const scoped_fcn_handle& fh2);
304
305protected:
306
307 void find_function ();
308
309 // The function we are handling.
310 octave_value m_fcn;
311
312 // List of parent function names. The first element is the name of
313 // m_fcn.
314 std::list<std::string> m_parentage;
315};
316
317class base_nested_fcn_handle : public base_fcn_handle
318{
319public:
320
321 // FIXME: octaveroot is temporary information used when loading
322 // handles. Can we avoid using it in the constructor?
323
324 base_nested_fcn_handle (const std::string& name = "",
325 const std::string& file = "",
326 const std::string& /*octaveroot*/ = "")
327 : base_fcn_handle (name, file)
328 { }
329
330 base_nested_fcn_handle (const octave_value& fcn, const std::string& name)
331 : base_fcn_handle (name), m_fcn (fcn)
332 { }
333
334 std::string type () const { return "nested"; }
335
337
338 bool is_nested () const { return true; }
339
340 // FIXME: These must go away. They don't do the right thing for
341 // scoping or overloads.
342 octave_function * function_value (bool = false)
343 {
344 return m_fcn.function_value ();
345 }
346
348 {
349 return m_fcn.user_function_value ();
350 }
351
352 octave_value fcn_val () { return m_fcn; }
353
354 virtual octave_value workspace () const = 0;
355
356 // Should be const.
358
359 bool save_ascii (std::ostream& os);
360
361 bool load_ascii (std::istream& is);
362
363 bool save_binary (std::ostream& os, bool save_as_floats);
364
365 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
366
367 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
368 bool save_as_floats);
369
370 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
371 octave_hdf5_id& type_hid);
372
373 void print_raw (std::ostream&, bool pr_as_read_syntax,
374 int current_print_indent_level) const;
375
376protected:
377
378 // The function we are handling.
379 octave_value m_fcn;
380};
381
382class nested_fcn_handle : public base_nested_fcn_handle
383{
384public:
385
386 // FIXME: octaveroot is temporary information used when loading
387 // handles. Can we avoid using it in the constructor?
388
389 nested_fcn_handle (const std::string& name = "",
390 const std::string& file = "",
391 const std::string& octaveroot = "")
392 : base_nested_fcn_handle (name, file, octaveroot)
393 { }
394
395 nested_fcn_handle (const octave_value& fcn, const std::string& name,
396 const std::shared_ptr<stack_frame>& stack_context)
397 : base_nested_fcn_handle (fcn, name), m_stack_context (stack_context)
398 {
399 if (m_stack_context)
400 m_stack_context->mark_closure_context ();
401 }
402
403 nested_fcn_handle (const nested_fcn_handle&) = default;
404
405 ~nested_fcn_handle () = default;
406
407 using base_nested_fcn_handle::is_nested;
408
409 bool is_nested (const std::shared_ptr<stack_frame>& frame) const
410 {
411 if (frame == m_stack_context)
412 return true;
413
414 // We need to check each of the access links of the context frame, for 'frame' too
415 auto nxt = m_stack_context->access_link ();
416 while (nxt)
417 {
418 if (nxt == frame)
419 return true;
420 nxt = nxt->access_link ();
421 }
422
423 return false;
424 }
425
426 nested_fcn_handle * clone () const
427 {
428 return new nested_fcn_handle (*this);
429 }
430
431 octave_value make_weak_nested_handle () const;
432
433 octave_value_list call (int nargout, const octave_value_list& args);
434
435 octave_value workspace () const;
436
437 friend bool is_equal_to (const nested_fcn_handle& fh1,
438 const nested_fcn_handle& fh2);
439
440 std::shared_ptr<stack_frame> stack_context () const
441 {
442 return m_stack_context;
443 }
444
445protected:
446
447 // Pointer to closure stack frames.
448 std::shared_ptr<stack_frame> m_stack_context;
449};
450
451class weak_nested_fcn_handle : public base_nested_fcn_handle
452{
453public:
454
455 weak_nested_fcn_handle (const nested_fcn_handle& nfh)
456 : base_nested_fcn_handle (nfh), m_stack_context (nfh.stack_context ())
457 { }
458
459 weak_nested_fcn_handle (const weak_nested_fcn_handle&) = default;
460
461 ~weak_nested_fcn_handle () = default;
462
463 weak_nested_fcn_handle * clone () const
464 {
465 return new weak_nested_fcn_handle (*this);
466 }
467
468 bool is_weak_nested () const { return true; }
469
470 octave_value_list call (int nargout, const octave_value_list& args);
471
472 octave_value workspace () const;
473
474 friend bool is_equal_to (const weak_nested_fcn_handle& fh1,
475 const weak_nested_fcn_handle& fh2);
476
477protected:
478
479 // Pointer to closure stack frames.
480 std::weak_ptr<stack_frame> m_stack_context;
481};
482
483class class_simple_fcn_handle : public base_fcn_handle
484{
485public:
486
487 // FIXME: octaveroot is temporary information used when loading
488 // handles. Can we avoid using it in the constructor?
489
490 class_simple_fcn_handle (const std::string& name,
491 const std::string& file,
492 const std::string& /*octaveroot*/)
493 : base_fcn_handle (name, file)
494 { }
495
496 // FIXME: is the method name supposed to be just the method name or
497 // also contain the object name?
498
499 class_simple_fcn_handle (const std::string& class_nm,
500 const std::string& meth_nm);
501
502 class_simple_fcn_handle (const octave_value& fcn,
503 const std::string& class_nm,
504 const std::string& meth_nm);
505
506 class_simple_fcn_handle (const octave_value& obj, const octave_value& fcn,
507 const std::string& class_nm,
508 const std::string& meth_nm);
509
510 class_simple_fcn_handle (const class_simple_fcn_handle&) = default;
511
512 ~class_simple_fcn_handle () = default;
513
514 class_simple_fcn_handle * clone () const
515 {
516 return new class_simple_fcn_handle (*this);
517 }
518
519 std::string type () const { return "classsimple"; }
520
521 bool is_class_simple () const { return true; }
522
523 octave_value_list call (int nargout, const octave_value_list& args);
524
525 // FIXME: These must go away. They don't do the right thing for
526 // scoping or overloads.
527 octave_function * function_value (bool = false)
528 {
529 // FIXME: Shouldn't the lookup rules here match those used in the
530 // call method?
531
532 if (m_fcn.is_defined ())
533 return m_fcn.function_value ();
534
536
537 // FIXME: is caching the correct thing to do?
538 // Cache this value so that the pointer will be valid as long as the
539 // function handle object is valid.
540
541 // FIXME: This should probably dispatch to the respective class method.
542 // But that breaks if a function handle is used in a class method with
543 // e.g. bsxfun with arguments of a different class (see bug #59661).
544 // m_fcn = symtab.find_method (m_name, m_dispatch_class);
545 m_fcn = symtab.find_function (m_name, octave_value_list ());
546
547 return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
548 }
549
551 {
552 return m_fcn.user_function_value ();
553 }
554
555 octave_value fcn_val () { return m_fcn; }
556
557 // Should be const.
559
560 std::string dispatch_class () const { return m_dispatch_class; }
561
562 bool save_ascii (std::ostream& os);
563
564 bool load_ascii (std::istream& is);
565
566 bool save_binary (std::ostream& os, bool save_as_floats);
567
568 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
569
570 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
571 bool save_as_floats);
572
573 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
574 octave_hdf5_id& type_hid);
575
576 void print_raw (std::ostream&, bool pr_as_read_syntax,
577 int current_print_indent_level) const;
578
579 friend bool is_equal_to (const class_simple_fcn_handle& fh1,
580 const class_simple_fcn_handle& fh2);
581
582protected:
583
584 // The object containing the method we are handing.
585 octave_value m_obj;
586
587 // The method we are handling.
588 octave_value m_fcn;
589
590 // Name of the class that m_fcn belongs to.
591 std::string m_dispatch_class;
592};
593
594// Handles to anonymous functions are similar to handles to nested
595// functions. If they are created in a context that contains nested
596// functions, then they store a link to the parent call stack frames
597// that are active when they are created. These call stack frames
598// (closure frames) provide access to variables needed by any nested
599// functions that are called from the anonymous function. Anonymous
600// functions also store a list of values from their parent scope
601// corresponding to the symbols in the anonymous function. This list
602// of values captures the variable values that are visible in the
603// scope where they are created.
604//
605// Note that because handles to anonymous and nested functions capture
606// call stack frames when they are created, they will cause deletion
607// of the values in those frames to be deferred until the handles to
608// the anonymous or nested functions are deleted.
609//
610// Would it be possible to avoid storing the closure frames for
611// handles to anonymous functions if we can determine that the
612// anonymous function has no unbound variables (or parameters, which
613// could be handles to nested functions?) or if it is not created in a
614// context that contains nested functions?
615//
616// Would it be possible to define anonymous functions as a special
617// type of nested function object that also has an variable
618// initialization list associated with it?
619
620class base_anonymous_fcn_handle : public base_fcn_handle
621{
622public:
623
624 static const std::string anonymous;
625
626 // Setting NAME here is a bit of a kluge to cope with a bad choice
627 // made to append the number of local variables to the @<anonymous>
628 // tag in the binary file format. See also the save_binary and
629 // load_binary functions.
630
631 base_anonymous_fcn_handle (const std::string& name = "")
632 : base_fcn_handle (name)
633 { }
634
635 base_anonymous_fcn_handle (const octave_value& fcn,
636 const stack_frame::local_vars_map& local_vars)
637 : base_fcn_handle (anonymous), m_fcn (fcn), m_local_vars (local_vars)
638 { }
639
640 base_anonymous_fcn_handle (const base_anonymous_fcn_handle&) = default;
641
642 ~base_anonymous_fcn_handle () = default;
643
644 std::string type () const { return "anonymous"; }
645
646 bool is_anonymous () const { return true; }
647
648 // FIXME: These must go away. They don't do the right thing for
649 // scoping or overloads.
650 octave_function * function_value (bool = false)
651 {
652 return m_fcn.function_value ();
653 }
654
656 {
657 return m_fcn.user_function_value ();
658 }
659
660 octave_value fcn_val () { return m_fcn; }
661
662 virtual octave_value workspace () const = 0;
663
664 // Should be const.
666
667 bool save_ascii (std::ostream& os);
668
669 bool load_ascii (std::istream& is);
670
671 bool save_binary (std::ostream& os, bool save_as_floats);
672
673 bool load_binary (std::istream& is, bool swap, mach_info::float_format fmt);
674
675 bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
676 bool save_as_floats);
677
678 bool load_hdf5 (octave_hdf5_id& group_hid, octave_hdf5_id& space_hid,
679 octave_hdf5_id& type_hid);
680
681 void print_raw (std::ostream&, bool pr_as_read_syntax,
682 int current_print_indent_level) const;
683
684 // Anonymous function handles are printed without a newline.
685 bool print_as_scalar () const { return false; }
686
687 bool parse (const std::string& fcn_text);
688
689protected:
690
691 // The function we are handling.
692 octave_value m_fcn;
693
694 // List of captured variable values for anonymous fucntions.
695 stack_frame::local_vars_map m_local_vars;
696};
697
698class anonymous_fcn_handle : public base_anonymous_fcn_handle
699{
700public:
701
702 using base_anonymous_fcn_handle::anonymous;
703
704 // Setting NAME here is a bit of a kluge to cope with a bad choice
705 // made to append the number of local variables to the @<anonymous>
706 // tag in the binary file format. See also the save_binary and
707 // load_binary functions.
708
709 anonymous_fcn_handle (const std::string& name = "")
710 : base_anonymous_fcn_handle (name), m_stack_context ()
711 { }
712
713 anonymous_fcn_handle (const octave_value& fcn,
714 const stack_frame::local_vars_map& local_vars,
715 const std::shared_ptr<stack_frame>& stack_context = std::shared_ptr<stack_frame> ());
716
717 anonymous_fcn_handle (const anonymous_fcn_handle&) = default;
718
719 ~anonymous_fcn_handle () = default;
720
721 anonymous_fcn_handle * clone () const
722 {
723 return new anonymous_fcn_handle (*this);
724 }
725
726 octave_value make_weak_anonymous_handle () const;
727
728 octave_value_list call (int nargout, const octave_value_list& args);
729
730 octave_value workspace () const;
731
732 friend bool is_equal_to (const anonymous_fcn_handle& fh1,
733 const anonymous_fcn_handle& fh2);
734
735 std::shared_ptr<stack_frame> stack_context () const
736 {
737 return m_stack_context;
738 }
739
740protected:
741
742 // Pointer to closure stack frames.
743 std::shared_ptr<stack_frame> m_stack_context;
744};
745
746class weak_anonymous_fcn_handle : public base_anonymous_fcn_handle
747{
748public:
749
750 using base_anonymous_fcn_handle::anonymous;
751
752 weak_anonymous_fcn_handle (const anonymous_fcn_handle& afh)
753 : base_anonymous_fcn_handle (afh), m_stack_context (afh.stack_context ())
754 { }
755
756 weak_anonymous_fcn_handle (const weak_anonymous_fcn_handle&) = default;
757
758 ~weak_anonymous_fcn_handle () = default;
759
760 weak_anonymous_fcn_handle * clone () const
761 {
762 return new weak_anonymous_fcn_handle (*this);
763 }
764
765 bool is_weak_anonymous () const { return true; }
766
767 octave_value_list call (int nargout, const octave_value_list& args);
768
769 octave_value workspace () const;
770
771 friend bool is_equal_to (const weak_anonymous_fcn_handle& fh1,
772 const weak_anonymous_fcn_handle& fh2);
773
774protected:
775
776 // Pointer to closure stack frames.
777 std::weak_ptr<stack_frame> m_stack_context;
778};
779
780extern bool is_equal_to (const anonymous_fcn_handle& fh1,
781 const anonymous_fcn_handle& fh2);
782
783static void
784err_invalid_fcn_handle (const std::string& name)
785{
786 error ("invalid function handle, unable to find function for @%s",
787 name.c_str ());
788}
789
792{
793 std::string type_str = type ();
794 error ("invalid conversion from %s handle to weak nestead handle",
795 type_str.c_str ());
796}
797
800{
801 std::string type_str = type ();
802 error ("invalid conversion from %s handle to weak anonymous handle",
803 type_str.c_str ());
804}
805
807base_fcn_handle::subsref (const std::string& type,
808 const std::list<octave_value_list>& idx,
809 int nargout)
810{
811 octave_value_list retval;
812
813 switch (type[0])
814 {
815 case '(':
816 {
817 int tmp_nargout = (type.length () > 1 && nargout == 0) ? 1 : nargout;
818
819 retval = call (tmp_nargout, idx.front ());
820 }
821 break;
822
823 case '{':
824 case '.':
825 error ("function handle cannot be indexed with %c", type[0]);
826
827 default:
828 error ("unexpected: index not '(', '{', or '.' in base_fcn_handle::subsref - please report this bug");
829 }
830
831 // FIXME: perhaps there should be an
832 // octave_value_list::next_subsref member function? See also
833 // octave_builtin::subsref.
834
835 if (idx.size () > 1)
836 retval = retval(0).next_subsref (nargout, type, idx);
837
838 return retval;
839}
840
842base_fcn_handle::convert_to_str_internal (bool, bool, char type) const
843{
844 std::ostringstream buf;
845 print_raw (buf, true, 0);
846 return octave_value (buf.str (), type);
847}
848
849bool
851{
852 unimplemented ("save", "text");
853
854 return true;
855}
856
857bool
859{
860 unimplemented ("load", "text");
861
862 return true;
863}
864
865bool
866base_fcn_handle::save_binary (std::ostream&, bool)
867{
868 unimplemented ("save", "binary");
869
870 return true;
871}
872
873bool
874base_fcn_handle::load_binary (std::istream&, bool, mach_info::float_format)
875{
876 unimplemented ("load", "binary");
877
878 return true;
879}
880
881bool
883{
884 unimplemented ("save", "hdf5");
885
886 return true;
887}
888
889bool
891{
892 unimplemented ("load", "hdf5");
893
894 return true;
895}
896
897void
898base_fcn_handle::warn_load (const char *file_type) const
899{
900 std::string obj_type = type ();
901
903 ("Octave:load-save-unavailable",
904 "%s: loading %s files not available in this version of Octave",
905 obj_type.c_str (), file_type);
906}
907
908void
909base_fcn_handle::warn_save (const char *file_type) const
910{
911 std::string obj_type = type ();
912
914 ("Octave:load-save-unavailable",
915 "%s: saving %s files not available in this version of Octave",
916 obj_type.c_str (), file_type);
917}
918
919void
920base_fcn_handle::unimplemented (const char *op, const char *fmt) const
921{
922 std::string htype = type ();
923
924 warning ("%s for %s handles with %s format is not implemented",
925 op, htype.c_str (), fmt);
926}
927
929invalid_fcn_handle::call (int, const octave_value_list&)
930{
931 error ("invalid call to invalid function handle");
932}
933
935internal_fcn_handle::call (int nargout, const octave_value_list& args)
936{
937 interpreter& interp = __get_interpreter__ ();
938
939 return interp.feval (m_fcn, args, nargout);
940}
941
943internal_fcn_handle::info ()
944{
946
947 m.setfield ("function", fcn_name ());
948 m.setfield ("type", type ());
949 m.setfield ("file", "");
950
951 return m;
952}
953
954bool
955is_equal_to (const internal_fcn_handle& fh1,
956 const internal_fcn_handle& fh2)
957{
958 if (fh1.m_name == fh2.m_name
959 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
960 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
961 else
962 return false;
963}
964
966simple_fcn_handle::call (int nargout, const octave_value_list& args)
967{
968 // FIXME: if m_name has a '.' in the name, lookup first component. If
969 // it is a classdef meta object, then build TYPE and IDX arguments and
970 // make a subsref call using them.
971
972 interpreter& interp = __get_interpreter__ ();
973
974 octave_value fcn_to_call;
975
976 // The following code is similar to part of
977 // tree_evaluator::visit_index_expression but simpler because it
978 // handles a more restricted case.
979
980 symbol_table& symtab = interp.get_symbol_table ();
981
982 std::size_t pos = m_name.find ('.');
983
984 if (pos != std::string::npos)
985 {
986 // FIXME: check to see which of these cases actually work in
987 // Octave and Matlab. For the last two, assume handle is
988 // created before object is defined as an object.
989 //
990 // We can have one of
991 //
992 // pkg-list . fcn (args)
993 // pkg-list . cls . meth (args)
994 // class-name . method (args)
995 // class-name . static-method (args)
996 // object . method (args)
997 // object . static-method (args)
998
999 // Evaluate package elements until we find a function,
1000 // classdef object, or classdef_meta object that is not a
1001 // package. An object may only appear as the first element,
1002 // then it must be followed directly by a function name.
1003
1004 std::size_t beg = 0;
1005 std::size_t end = pos;
1006
1007 std::vector<std::string> idx_elts;
1008
1009 while (true)
1010 {
1011 end = m_name.find ('.', beg);
1012
1013 idx_elts.push_back (m_name.substr (beg, end-beg));
1014
1015 if (end == std::string::npos)
1016 break;
1017
1018 beg = end+1;
1019 }
1020
1021 std::size_t n_elts = idx_elts.size ();
1022
1023 bool have_object = false;
1024 octave_value partial_expr_val;
1025
1026 // Lazy evaluation. The first element was not known to be defined
1027 // as an object in the scope where the handle was created. See if
1028 // there is a definition in the current scope.
1029
1030 partial_expr_val = interp.varval (idx_elts[0]);
1031
1032 if (partial_expr_val.is_defined ())
1033 {
1034 if (! partial_expr_val.is_classdef_object () || n_elts != 2)
1035 err_invalid_fcn_handle (m_name);
1036
1037 have_object = true;
1038 }
1039 else
1040 partial_expr_val = symtab.find_function (idx_elts[0], ovl ());
1041
1042 std::string type;
1043 std::list<octave_value_list> arg_list;
1044
1045 for (std::size_t i = 1; i < n_elts; i++)
1046 {
1047 if (partial_expr_val.is_package ())
1048 {
1049 if (have_object)
1050 err_invalid_fcn_handle (m_name);
1051
1052 type = ".";
1053 arg_list.push_back (ovl (idx_elts[i]));
1054
1055 try
1056 {
1057 // Silently ignore extra output values.
1058
1059 octave_value_list tmp_list
1060 = partial_expr_val.subsref (type, arg_list, 0);
1061
1062 partial_expr_val
1063 = tmp_list.length () ? tmp_list(0) : octave_value ();
1064
1065 if (partial_expr_val.is_cs_list ())
1066 err_invalid_fcn_handle (m_name);
1067
1068 arg_list.clear ();
1069 }
1070 catch (const index_exception&)
1071 {
1072 err_invalid_fcn_handle (m_name);
1073 }
1074 }
1075 else if (have_object || partial_expr_val.is_classdef_meta ())
1076 {
1077 // Object or class name must be the next to the last
1078 // element (it was the previous one, so if this is the
1079 // final element, it should be a classdef method,
1080 // but we'll let the classdef or classdef_meta subsref
1081 // function sort that out.
1082
1083 if (i != n_elts-1)
1084 err_invalid_fcn_handle (m_name);
1085
1086 type = ".(";
1087 arg_list.push_back (ovl (idx_elts[i]));
1088 arg_list.push_back (args);
1089
1090 return partial_expr_val.subsref (type, arg_list, nargout);
1091 }
1092 else
1093 err_invalid_fcn_handle (m_name);
1094 }
1095
1096 // If we get here, we must have a function to call.
1097
1098 if (! partial_expr_val.is_function ())
1099 err_invalid_fcn_handle (m_name);
1100
1101 fcn_to_call = partial_expr_val;
1102 }
1103 else
1104 {
1105 // No "." in the name.
1106
1107 // Perform function lookup given current arguments. We'll need
1108 // to do this regardless of whether a function was found when
1109 // the handle was created.
1110
1111 octave_value ov_fcn = symtab.find_function (m_name, args);
1112
1113 if (m_fcn.is_defined ())
1114 {
1115 // A simple function was found when the handle was created.
1116 // Use that unless we find a class method to override it.
1117
1118 fcn_to_call = m_fcn;
1119
1120 if (ov_fcn.is_defined ())
1121 {
1122 octave_function *fcn = ov_fcn.function_value ();
1123
1124 std::string dispatch_class = fcn->dispatch_class ();
1125
1126 if (fcn->is_class_method ())
1127 {
1128 // Function found through lookup is a class method
1129 // so use it instead of the simple one found when
1130 // the handle was created.
1131
1132 fcn_to_call = ov_fcn;
1133 }
1134 }
1135 }
1136 else
1137 {
1138 // There was no simple function found when the handle was
1139 // created so use the one found here (if any).
1140
1141 fcn_to_call = ov_fcn;
1142 }
1143 }
1144
1145 if (! fcn_to_call.is_defined ())
1146 err_invalid_fcn_handle (m_name);
1147
1148 return interp.feval (fcn_to_call, args, nargout);
1149}
1150
1152simple_fcn_handle::function_value (bool)
1153{
1154 // FIXME: Shouldn't the lookup rules here match those used in the
1155 // call method?
1156
1157 if (m_fcn.is_defined ())
1158 return m_fcn.function_value ();
1159
1161
1162 // FIXME: is caching the correct thing to do?
1163 // Cache this value so that the pointer will be valid as long as the
1164 // function handle object is valid.
1165
1166 m_fcn = symtab.find_function (m_name, octave_value_list ());
1167
1168 return m_fcn.is_defined () ? m_fcn.function_value () : nullptr;
1169}
1170
1172simple_fcn_handle::user_function_value (bool)
1173{
1174 // FIXME: Shouldn't the lookup rules here match those used in the
1175 // call method?
1176
1177 if (m_fcn.is_defined ())
1178 return m_fcn.user_function_value ();
1179
1181
1182 // FIXME: is caching the correct thing to do?
1183 // Cache this value so that the pointer will be valid as long as the
1184 // function handle object is valid.
1185
1186 m_fcn = symtab.find_user_function (m_name);
1187
1188 return m_fcn.is_defined () ? m_fcn.user_function_value () : nullptr;
1189}
1190
1192simple_fcn_handle::fcn_val ()
1193{
1194 if (m_fcn.is_defined ())
1195 return m_fcn;
1196
1198
1199 // FIXME: is caching the correct thing to do?
1200 // Cache this value so that the pointer will be valid as long as the
1201 // function handle object is valid.
1202
1203 m_fcn = symtab.find_user_function (m_name);
1204
1205 return m_fcn;
1206}
1207
1209simple_fcn_handle::info ()
1210{
1212
1213 m.setfield ("function", fcn_name ());
1214 m.setfield ("type", type ());
1215 // When is FILE defined for simple function handles?
1216 m.setfield ("file", file ());
1217
1218 return m;
1219}
1220
1221bool
1222simple_fcn_handle::save_ascii (std::ostream& os)
1223{
1224 os << "# octaveroot: " << config::octave_exec_home () << "\n";
1225
1226 std::string fnm = file ();
1227 if (! fnm.empty ())
1228 os << "# path: " << fnm << "\n";
1229
1230 os << "# subtype: " << type () << "\n";
1231
1232 os << m_name << "\n";
1233
1234 return true;
1235}
1236
1237bool
1238simple_fcn_handle::load_ascii (std::istream& is)
1239{
1240 // FIXME: If m_file is not empty, try to load the file and define
1241 // the function? Is it an error if that fails? Or should this job
1242 // always be deferred until the handle is used?
1243
1244 return is.good ();
1245}
1246
1247bool
1248simple_fcn_handle::save_binary (std::ostream& os, bool)
1249{
1250 std::ostringstream nmbuf;
1251
1252 // When is FILE defined for simple function handles?
1253 std::string fnm;
1254
1255 nmbuf << m_name << "@<simple>\n" << config::octave_exec_home ()
1256 << "\n" << fnm;
1257
1258 std::string buf_str = nmbuf.str ();
1259 int32_t tmp = buf_str.length ();
1260 os.write (reinterpret_cast<char *> (&tmp), 4);
1261 os.write (buf_str.c_str (), buf_str.length ());
1262
1263 return true;
1264}
1265
1266bool
1267simple_fcn_handle::load_binary (std::istream& is, bool,
1268 mach_info::float_format)
1269{
1270 return is.good ();
1271}
1272
1273bool
1274simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
1275 bool)
1276{
1277#if defined (HAVE_HDF5)
1278
1279 bool retval = true;
1280
1281 octave_hdf5_id group_hid = -1;
1282#if defined (HAVE_HDF5_18)
1283 group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1285#else
1286 group_hid = H5Gcreate (loc_id, name, 0);
1287#endif
1288 if (group_hid < 0)
1289 return false;
1290
1291 octave_hdf5_id space_hid, data_hid, type_hid;
1292 space_hid = data_hid = type_hid = -1;
1293
1294 // attach the type of the variable
1295 type_hid = H5Tcopy (H5T_C_S1);
1296 H5Tset_size (type_hid, m_name.length () + 1);
1297 if (type_hid < 0)
1298 {
1299 H5Gclose (group_hid);
1300 return false;
1301 }
1302
1303 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
1304 hdims[0] = 0;
1305 hdims[1] = 0;
1306 space_hid = H5Screate_simple (0, hdims, nullptr);
1307 if (space_hid < 0)
1308 {
1309 H5Tclose (type_hid);
1310 H5Gclose (group_hid);
1311 return false;
1312 }
1313#if defined (HAVE_HDF5_18)
1314 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
1317#else
1318 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
1320#endif
1321 if (data_hid < 0
1322 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
1323 octave_H5P_DEFAULT, m_name.c_str ()) < 0)
1324 {
1325 H5Sclose (space_hid);
1326 H5Tclose (type_hid);
1327 H5Gclose (group_hid);
1328 return false;
1329 }
1330 H5Dclose (data_hid);
1331
1332 std::string octaveroot = config::octave_exec_home ();
1333
1334 // When is FILE defined for simple fucntion handles?
1335 std::string fpath;
1336
1337 H5Sclose (space_hid);
1338 hdims[0] = 1;
1339 hdims[1] = octaveroot.length ();
1340 space_hid = H5Screate_simple (0, hdims, nullptr);
1341 if (space_hid < 0)
1342 {
1343 H5Tclose (type_hid);
1344 H5Gclose (group_hid);
1345 return false;
1346 }
1347
1348 H5Tclose (type_hid);
1349 type_hid = H5Tcopy (H5T_C_S1);
1350 H5Tset_size (type_hid, octaveroot.length () + 1);
1351 octave_hdf5_id a_id;
1352#if defined (HAVE_HDF5_18)
1353 a_id = H5Acreate (group_hid, "OCTAVEROOT", type_hid, space_hid,
1355#else
1356 a_id = H5Acreate (group_hid, "OCTAVEROOT", type_hid, space_hid,
1358#endif
1359
1360 if (a_id >= 0)
1361 {
1362 retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0);
1363
1364 H5Aclose (a_id);
1365 }
1366 else
1367 {
1368 H5Sclose (space_hid);
1369 H5Tclose (type_hid);
1370 H5Gclose (group_hid);
1371 return false;
1372 }
1373
1374 H5Sclose (space_hid);
1375 hdims[0] = 1;
1376 hdims[1] = fpath.length ();
1377 space_hid = H5Screate_simple (0, hdims, nullptr);
1378 if (space_hid < 0)
1379 {
1380 H5Tclose (type_hid);
1381 H5Gclose (group_hid);
1382 return false;
1383 }
1384
1385 H5Tclose (type_hid);
1386 type_hid = H5Tcopy (H5T_C_S1);
1387 H5Tset_size (type_hid, fpath.length () + 1);
1388
1389#if defined (HAVE_HDF5_18)
1390 a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
1392#else
1393 a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid,
1395#endif
1396
1397 if (a_id >= 0)
1398 {
1399 retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0);
1400
1401 H5Aclose (a_id);
1402 }
1403 else
1404 retval = false;
1405
1406 H5Sclose (space_hid);
1407 H5Tclose (type_hid);
1408 H5Gclose (group_hid);
1409
1410 return retval;
1411
1412#else
1413
1414 octave_unused_parameter (loc_id);
1415 octave_unused_parameter (name);
1416
1417 warn_save ("hdf5");
1418
1419 return false;
1420
1421#endif
1422}
1423
1424bool
1425simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1426 octave_hdf5_id& space_hid,
1427 octave_hdf5_id& type_hid)
1428{
1429#if defined (HAVE_HDF5)
1430
1431 unimplemented ("load", "hdf5");
1432
1433 octave_unused_parameter (group_hid);
1434 octave_unused_parameter (space_hid);
1435 octave_unused_parameter (type_hid);
1436
1437 return true;
1438
1439#else
1440
1441 octave_unused_parameter (group_hid);
1442 octave_unused_parameter (space_hid);
1443 octave_unused_parameter (type_hid);
1444
1445 return false;
1446
1447#endif
1448}
1449
1450void
1451simple_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax,
1452 int current_print_indent_level) const
1453{
1454 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1455 current_print_indent_level);
1456}
1457
1458bool
1459is_equal_to (const simple_fcn_handle& fh1, const simple_fcn_handle& fh2)
1460{
1461 if (fh1.m_name == fh2.m_name)
1462 {
1463 if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1464 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1465
1466 if (fh1.m_fcn.is_undefined () && fh2.m_fcn.is_undefined ())
1467 return true;
1468 }
1469
1470 return false;
1471}
1472
1473scoped_fcn_handle::scoped_fcn_handle (const octave_value& fcn,
1474 const std::string& name,
1475 const std::list<std::string>& parentage)
1476 : base_fcn_handle (name), m_fcn (fcn), m_parentage (parentage)
1477{
1478 // FIXME: should it be an error if FCN is undefined?
1479
1480 if (m_fcn.is_defined ())
1481 {
1482 octave_function *oct_fcn = m_fcn.function_value ();
1483
1484 if (oct_fcn)
1485 m_file = oct_fcn->fcn_file_name ();
1486 }
1487
1488 m_parentage.push_front (name);
1489}
1490
1492scoped_fcn_handle::call (int nargout, const octave_value_list& args)
1493{
1494 // FIXME: we aren't really using the scope yet. Hmm.
1495
1496 interpreter& interp = __get_interpreter__ ();
1497
1498 if (! m_fcn.is_defined ())
1499 {
1500 // Try to find it?
1501
1502 find_function ();
1503 }
1504
1505 if (! m_fcn.is_defined ())
1506 err_invalid_fcn_handle (m_name);
1507
1508 return interp.feval (m_fcn, args, nargout);
1509}
1510
1512scoped_fcn_handle::info ()
1513{
1515
1516 m.setfield ("function", fcn_name ());
1517 m.setfield ("type", type ());
1518 m.setfield ("file", file ());
1519
1520 m.setfield ("parentage", Cell (m_parentage));
1521
1522 return m;
1523}
1524
1525bool
1526scoped_fcn_handle::save_ascii (std::ostream& os)
1527{
1528 os << "# octaveroot: " << config::octave_exec_home () << "\n";
1529
1530 std::string fnm = file ();
1531 if (! fnm.empty ())
1532 os << "# path: " << fnm << "\n";
1533
1534 os << "# subtype: " << type () << "\n";
1535
1536 os << m_name << "\n";
1537
1538 octave_value tmp = Cell (m_parentage);
1539 tmp.save_ascii (os);
1540
1541 return os.good ();
1542}
1543
1544bool
1545scoped_fcn_handle::load_ascii (std::istream& is)
1546{
1547 octave_cell ov_cell;
1548 ov_cell.load_ascii (is);
1549
1550 if (ov_cell.iscellstr ())
1551 {
1552 Array<std::string> cellstr_val = ov_cell.cellstr_value ();
1553
1554 for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
1555 m_parentage.push_back (cellstr_val(i));
1556 }
1557
1558 return is.good ();
1559}
1560
1561bool
1562scoped_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
1563{
1564 std::ostringstream nmbuf;
1565
1566 std::string fnm = file ();
1567
1568 nmbuf << m_name << "@<scopedfunction>\n" << config::octave_exec_home ()
1569 << "\n" << fnm;
1570
1571 std::string buf_str = nmbuf.str ();
1572 int32_t len = buf_str.length ();
1573 os.write (reinterpret_cast<char *> (&len), 4);
1574 os.write (buf_str.c_str (), buf_str.length ());
1575
1576 octave_value tmp = Cell (m_parentage);
1577 tmp.save_binary (os, save_as_floats);
1578
1579 return os.good ();
1580}
1581
1582bool
1583scoped_fcn_handle::load_binary (std::istream& is, bool swap,
1584 mach_info::float_format fmt)
1585{
1586 octave_cell ov_cell;
1587 ov_cell.load_binary (is, swap, fmt);
1588
1589 if (ov_cell.iscellstr ())
1590 {
1591 Array<std::string> cellstr_val = ov_cell.cellstr_value ();
1592
1593 for (octave_idx_type i = 0; i < cellstr_val.numel (); i++)
1594 m_parentage.push_back (cellstr_val(i));
1595 }
1596
1597 return is.good ();
1598}
1599
1600bool
1601scoped_fcn_handle::save_hdf5 (octave_hdf5_id loc_id, const char *name,
1602 bool)
1603{
1604#if defined (HAVE_HDF5)
1605
1606 unimplemented ("save", "hdf5");
1607
1608 // FIXME: save parentage.
1609
1610 octave_unused_parameter (loc_id);
1611 octave_unused_parameter (name);
1612
1613 return true;
1614
1615#else
1616
1617 octave_unused_parameter (loc_id);
1618 octave_unused_parameter (name);
1619
1620 warn_save ("hdf5");
1621
1622 return false;
1623
1624#endif
1625}
1626
1627bool
1628scoped_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1629 octave_hdf5_id& space_hid,
1630 octave_hdf5_id& type_hid)
1631{
1632#if defined (HAVE_HDF5)
1633
1634 unimplemented ("load", "hdf5");
1635
1636 // FIXME: load parentage.
1637
1638 octave_unused_parameter (group_hid);
1639 octave_unused_parameter (space_hid);
1640 octave_unused_parameter (type_hid);
1641
1642 return true;
1643
1644#else
1645
1646 octave_unused_parameter (group_hid);
1647 octave_unused_parameter (space_hid);
1648 octave_unused_parameter (type_hid);
1649
1650 return false;
1651
1652#endif
1653}
1654
1655void
1656scoped_fcn_handle::print_raw (std::ostream& os,
1657 bool pr_as_read_syntax,
1658 int current_print_indent_level) const
1659{
1660 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1661 current_print_indent_level);
1662}
1663
1664bool
1665is_equal_to (const scoped_fcn_handle& fh1, const scoped_fcn_handle& fh2)
1666{
1667 if (fh1.m_name == fh2.m_name
1668 && fh2.m_parentage == fh2.m_parentage
1669 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1670 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1671 else
1672 return false;
1673}
1674
1675void
1676scoped_fcn_handle::find_function ()
1677{
1678 // Since a scoped function is not visible by itself, try to load the
1679 // file named in m_file then find and define the scoped function.
1680 // It is not an error if this fails. We can report later that the
1681 // handle is invalid.
1682
1684
1685 if (m_parentage.size () == 1)
1686 {
1687 std::string dir_name = sys::file_ops::dirname (m_file);
1688
1689 std::size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ());
1690
1691 if (pos != std::string::npos)
1692 dir_name = dir_name.substr (0, pos);
1693 else if (dir_name == "private")
1694 dir_name = ".";
1695
1696 std::string fcn_name = m_parentage.front ();
1697
1698 // FIXME: Does dir_name need to be in the load path for this to work?
1699
1700 m_fcn = symtab.find_private_function (dir_name, m_name);
1701
1702 // FIXME: Verify that it is a private function?
1703 }
1704 else
1705 {
1706 std::string primary_parent_name = m_parentage.back ();
1707
1708 octave_value ov_parent_fcn
1709 = symtab.find_user_function (primary_parent_name);
1710
1711 if (ov_parent_fcn.is_defined ())
1712 {
1713 octave_user_function *fcn = ov_parent_fcn.user_function_value ();
1714
1715 if (fcn)
1716 {
1717 std::string file_name = fcn->fcn_file_name ();
1718
1719 std::string oct_home = config::octave_exec_home ();
1720
1721 if (file_name.substr (0, oct_home.size ()) == oct_home)
1722 file_name = file_name.substr (oct_home.size ());
1723
1724 octave_value subfcn = fcn->find_subfunction (m_name);
1725
1726 if (subfcn.is_defined ())
1727 m_fcn = subfcn;
1728 }
1729 }
1730 }
1731}
1732
1734base_nested_fcn_handle::info ()
1735{
1737
1738 m.setfield ("function", fcn_name ());
1739 m.setfield ("type", type ());
1740 m.setfield ("file", "");
1741 m.setfield ("workspace", workspace ());
1742
1743 return m;
1744}
1745
1746// FIXME: For save, we need a way to save the (possibly shared)
1747// workspace. For load, we need a way to load and link to the
1748// (possibly shared) workspace that was saved.
1749//
1750// Since a nested function is not visible by itself, do we need to try
1751// to load the file named in m_file then find and define the function?
1752// Is it an error if that fails? Or should this job always be
1753// deferred until the handle is used?
1754
1755bool
1756base_nested_fcn_handle::save_ascii (std::ostream& os)
1757{
1758 unimplemented ("save", "text");
1759
1760 octave_unused_parameter (os);
1761
1762 return true;
1763}
1764
1765bool
1766base_nested_fcn_handle::load_ascii (std::istream& is)
1767{
1768 unimplemented ("load", "text");
1769
1770 octave_unused_parameter (is);
1771
1772 return true;
1773}
1774
1775bool
1776base_nested_fcn_handle::save_binary (std::ostream& os,
1777 bool save_as_floats)
1778{
1779 unimplemented ("save", "binary");
1780
1781 octave_unused_parameter (os);
1782 octave_unused_parameter (save_as_floats);
1783
1784 return true;
1785}
1786
1787bool
1788base_nested_fcn_handle::load_binary (std::istream& is, bool swap,
1789 mach_info::float_format fmt)
1790{
1791 unimplemented ("load", "binary");
1792
1793 octave_unused_parameter (is);
1794 octave_unused_parameter (swap);
1795 octave_unused_parameter (fmt);
1796
1797 return true;
1798}
1799
1800bool
1801base_nested_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
1802 const char *name, bool)
1803{
1804#if defined (HAVE_HDF5)
1805
1806 unimplemented ("save", "hdf5");
1807
1808 octave_unused_parameter (loc_id);
1809 octave_unused_parameter (name);
1810
1811 return true;
1812
1813#else
1814
1815 octave_unused_parameter (loc_id);
1816 octave_unused_parameter (name);
1817
1818 warn_save ("hdf5");
1819
1820 return false;
1821
1822#endif
1823}
1824
1825bool
1826base_nested_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
1827 octave_hdf5_id& space_hid,
1828 octave_hdf5_id& type_hid)
1829{
1830#if defined (HAVE_HDF5)
1831
1832 unimplemented ("load", "hdf5");
1833
1834 octave_unused_parameter (group_hid);
1835 octave_unused_parameter (space_hid);
1836 octave_unused_parameter (type_hid);
1837
1838 return true;
1839
1840#else
1841
1842 octave_unused_parameter (group_hid);
1843 octave_unused_parameter (space_hid);
1844 octave_unused_parameter (type_hid);
1845
1846 return false;
1847
1848#endif
1849}
1850
1851void
1852base_nested_fcn_handle::print_raw (std::ostream& os,
1853 bool pr_as_read_syntax,
1854 int current_print_indent_level) const
1855{
1856 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
1857 current_print_indent_level);
1858}
1859
1861nested_fcn_handle::make_weak_nested_handle () const
1862{
1864 (new weak_nested_fcn_handle (*this)));
1865}
1866
1868nested_fcn_handle::call (int nargout, const octave_value_list& args)
1869{
1871
1872 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
1873
1874 tw.push_stack_frame (oct_usr_fcn, m_stack_context);
1875
1876 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
1877
1878 return oct_usr_fcn->execute (tw, nargout, args);
1879}
1880
1882nested_fcn_handle::workspace () const
1883{
1884 return m_stack_context->workspace ();
1885}
1886
1887bool
1888is_equal_to (const nested_fcn_handle& fh1, const nested_fcn_handle& fh2)
1889{
1890 if (fh1.m_name == fh2.m_name
1891 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1892 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1893 else
1894 return false;
1895}
1896
1898weak_nested_fcn_handle::call (int nargout, const octave_value_list& args)
1899{
1901
1902 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
1903
1904 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
1905
1906 tw.push_stack_frame (oct_usr_fcn, frames);
1907
1908 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
1909
1910 return oct_usr_fcn->execute (tw, nargout, args);
1911}
1912
1914weak_nested_fcn_handle::workspace () const
1915{
1916 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
1917
1918 return frames ? frames->workspace () : octave_value ();
1919}
1920
1921bool
1922is_equal_to (const weak_nested_fcn_handle& fh1,
1923 const weak_nested_fcn_handle& fh2)
1924{
1925 if (fh1.m_name == fh2.m_name
1926 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
1927 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
1928 else
1929 return false;
1930}
1931
1932class_simple_fcn_handle::class_simple_fcn_handle (const std::string& class_nm,
1933 const std::string& meth_nm)
1934 : base_fcn_handle (meth_nm), m_obj (), m_fcn (),
1935 m_dispatch_class (class_nm)
1936{ }
1937
1938class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& fcn,
1939 const std::string& class_nm,
1940 const std::string& meth_nm)
1941 : base_fcn_handle (meth_nm), m_obj (), m_fcn (fcn),
1942 m_dispatch_class (class_nm)
1943{ }
1944
1945class_simple_fcn_handle::class_simple_fcn_handle (const octave_value& obj,
1946 const octave_value& fcn,
1947 const std::string& class_nm,
1948 const std::string& meth_nm)
1949 : base_fcn_handle (meth_nm), m_obj (obj), m_fcn (fcn),
1950 m_dispatch_class (class_nm)
1951{ }
1952
1954class_simple_fcn_handle::call (int nargout, const octave_value_list& args)
1955{
1956 interpreter& interp = __get_interpreter__ ();
1957
1958 if (m_obj.is_defined ())
1959 {
1960 octave_value_list tmp_args = args;
1961 tmp_args.prepend (m_obj);
1962
1963 return interp.feval (m_fcn, tmp_args, nargout);
1964 }
1965
1966 // FIXME: is this the best approach? Should we be saving current
1967 // dispatch class and restoring that value instead of
1968 // unconditionally setting it to "" when we return from this
1969 // function?
1970
1971 tree_evaluator& tw = interp.get_evaluator ();
1972
1973 unwind_action act ([&tw] () { tw.set_dispatch_class (""); });
1974
1975 tw.set_dispatch_class (m_dispatch_class);
1976
1977 if (m_fcn.is_defined ())
1978 return interp.feval (m_fcn, args, nargout);
1979
1980 return interp.feval (fcn_name (), args, nargout);
1981}
1982
1984class_simple_fcn_handle::info ()
1985{
1987
1988 m.setfield ("function", fcn_name ());
1989 m.setfield ("type", type ());
1990 m.setfield ("file", "");
1991 m.setfield ("class", dispatch_class ());
1992
1993 return m;
1994}
1995
1996// FIXME: Since a class method is not visible by itself, do we need to
1997// try to load the file named in m_file then find and define the
1998// function? Is it an error if that fails? Or should this job always
1999// be deferred until the handle is used?
2000
2001bool
2002class_simple_fcn_handle::save_ascii (std::ostream& os)
2003{
2004 unimplemented ("save", "text");
2005
2006 octave_unused_parameter (os);
2007
2008 return true;
2009}
2010
2011bool
2012class_simple_fcn_handle::load_ascii (std::istream& is)
2013{
2014 unimplemented ("load", "text");
2015
2016 octave_unused_parameter (is);
2017
2018 return true;
2019}
2020
2021bool
2022class_simple_fcn_handle::save_binary (std::ostream& os,
2023 bool save_as_floats)
2024{
2025 unimplemented ("save", "binary");
2026
2027 octave_unused_parameter (os);
2028 octave_unused_parameter (save_as_floats);
2029
2030 return true;
2031}
2032
2033bool
2034class_simple_fcn_handle::load_binary (std::istream& is, bool swap,
2035 mach_info::float_format fmt)
2036{
2037 unimplemented ("load", "binary");
2038
2039 octave_unused_parameter (is);
2040 octave_unused_parameter (swap);
2041 octave_unused_parameter (fmt);
2042
2043 return true;
2044}
2045
2046bool
2047class_simple_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
2048 const char *name, bool)
2049{
2050#if defined (HAVE_HDF5)
2051
2052 unimplemented ("save", "hdf5");
2053
2054 octave_unused_parameter (loc_id);
2055 octave_unused_parameter (name);
2056
2057 return true;
2058
2059#else
2060
2061 octave_unused_parameter (loc_id);
2062 octave_unused_parameter (name);
2063
2064 warn_save ("hdf5");
2065
2066 return false;
2067
2068#endif
2069}
2070
2071bool
2072class_simple_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
2073 octave_hdf5_id& space_hid,
2074 octave_hdf5_id& type_hid)
2075{
2076#if defined (HAVE_HDF5)
2077
2078 unimplemented ("load", "hdf5");
2079
2080 octave_unused_parameter (group_hid);
2081 octave_unused_parameter (space_hid);
2082 octave_unused_parameter (type_hid);
2083
2084 return true;
2085
2086#else
2087
2088 octave_unused_parameter (group_hid);
2089 octave_unused_parameter (space_hid);
2090 octave_unused_parameter (type_hid);
2091
2092 return false;
2093
2094#endif
2095}
2096
2097void
2098class_simple_fcn_handle::print_raw (std::ostream& os,
2099 bool pr_as_read_syntax,
2100 int current_print_indent_level) const
2101{
2102 octave_print_internal (os, '@' + m_name, pr_as_read_syntax,
2103 current_print_indent_level);
2104}
2105
2106bool
2107is_equal_to (const class_simple_fcn_handle& fh1,
2108 const class_simple_fcn_handle& fh2)
2109{
2110 // FIXME: Also need to check object values are equivalent?
2111
2112 if (fh1.m_name == fh2.m_name
2113 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2114 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2115 else
2116 return false;
2117}
2118
2119const std::string base_anonymous_fcn_handle::anonymous ("@<anonymous>");
2120
2122base_anonymous_fcn_handle::info ()
2123{
2125
2126 std::ostringstream buf;
2127 print_raw (buf, true, 0);
2128 m.setfield ("function", buf.str ());
2129
2130 m.setfield ("type", type ());
2131 m.setfield ("file", "");
2132 m.setfield ("workspace", workspace ());
2133 m.setfield ("within_file_path", "");
2134
2135 return m;
2136}
2137
2138bool
2139base_anonymous_fcn_handle::save_ascii (std::ostream& os)
2140{
2141 // FIXME: can we ensure that m_fcn is always defined?
2142
2143 if (m_fcn.is_undefined ())
2144 return false;
2145
2146 os << m_name << "\n";
2147
2148 print_raw (os, true, 0);
2149 os << "\n";
2150
2151 std::size_t varlen = m_local_vars.size ();
2152
2153 if (varlen > 0)
2154 {
2155 os << "# length: " << varlen << "\n";
2156
2157 for (const auto& nm_val : m_local_vars)
2158 {
2159 if (! save_text_data (os, nm_val.second, nm_val.first, false, 0))
2160 return ! os.fail ();
2161 }
2162 }
2163
2164 return true;
2165}
2166
2167bool
2168base_anonymous_fcn_handle::load_ascii (std::istream& is)
2169{
2170 octave::skip_preceeding_newline (is);
2171
2172 std::string buf;
2173
2174 if (is)
2175 {
2176 // Get a line of text whitespace characters included, leaving
2177 // newline in the stream.
2178
2179 buf = octave::read_until_newline (is, true);
2180 }
2181
2182 std::streampos pos = is.tellg ();
2183
2184 // Set up temporary scope to use for evaluating the text that
2185 // defines the anonymous function.
2186
2187 interpreter& interp = __get_interpreter__ ();
2188
2189 tree_evaluator& tw = interp.get_evaluator ();
2190
2191 tw.push_dummy_scope (buf);
2192 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2193
2194 octave_idx_type len = 0;
2195
2196 if (extract_keyword (is, "length", len, true) && len >= 0)
2197 {
2198 if (len > 0)
2199 {
2200 for (octave_idx_type i = 0; i < len; i++)
2201 {
2202 octave_value t2;
2203 bool dummy;
2204
2205 std::string name = read_text_data (is, "", dummy, t2, i);
2206
2207 if (! is)
2208 error ("load: failed to load anonymous function handle");
2209
2210 m_local_vars[name] = t2;
2211 }
2212 }
2213 }
2214 else
2215 {
2216 is.seekg (pos);
2217 is.clear ();
2218 }
2219
2220 if (is)
2221 return parse (buf);
2222
2223 return false;
2224}
2225
2226bool
2227base_anonymous_fcn_handle::save_binary (std::ostream& os,
2228 bool save_as_floats)
2229{
2230 // FIXME: can we ensure that m_fcn is always defined?
2231
2232 if (m_fcn.is_undefined ())
2233 return false;
2234
2235 std::ostringstream nmbuf;
2236
2237 std::size_t varlen = m_local_vars.size ();
2238
2239 nmbuf << anonymous;
2240 if (varlen > 0)
2241 nmbuf << ' ' << varlen;
2242
2243 std::string buf_str = nmbuf.str ();
2244 int32_t tmp = buf_str.length ();
2245 os.write (reinterpret_cast<char *> (&tmp), 4);
2246 os.write (buf_str.c_str (), buf_str.length ());
2247
2248 std::ostringstream buf;
2249 print_raw (buf, true, 0);
2250 std::string stmp = buf.str ();
2251 tmp = stmp.length ();
2252 os.write (reinterpret_cast<char *> (&tmp), 4);
2253 os.write (stmp.c_str (), stmp.length ());
2254
2255 if (varlen > 0)
2256 {
2257 for (const auto& nm_val : m_local_vars)
2258 {
2259 if (! save_binary_data (os, nm_val.second, nm_val.first,
2260 "", 0, save_as_floats))
2261 return ! os.fail ();
2262 }
2263 }
2264
2265 return true;
2266}
2267
2268bool
2269base_anonymous_fcn_handle::load_binary (std::istream& is, bool swap,
2270 mach_info::float_format fmt)
2271{
2272 // Read extra characters in m_name as the number of local variable
2273 // values in this anonymous function.
2274
2275 octave_idx_type len = 0;
2276 std::size_t anl = anonymous.length ();
2277 if (m_name.length () > anl)
2278 {
2279 std::istringstream nm_is (m_name.substr (anl));
2280 nm_is >> len;
2281
2282 // Anonymous functions don't have names. We just used this
2283 // string as temporary storage to pass the number of local
2284 // variable values.
2285
2286 m_name = "";
2287 }
2288
2289 int32_t tmp;
2290
2291 if (! is.read (reinterpret_cast<char *> (&tmp), 4))
2292 return false;
2293 if (swap)
2294 swap_bytes<4> (&tmp);
2295
2296 OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1);
2297 // is.get (ctmp2, tmp+1, 0); caused is.eof () to be true though
2298 // effectively not reading over file end
2299 is.read (ctmp2, tmp);
2300 ctmp2[tmp] = 0;
2301
2302 // Set up temporary scope to use for evaluating the text that
2303 // defines the anonymous function.
2304
2305 interpreter& interp = __get_interpreter__ ();
2306
2307 tree_evaluator& tw = interp.get_evaluator ();
2308
2309 tw.push_dummy_scope (ctmp2);
2310 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2311
2312 if (len > 0)
2313 {
2314 for (octave_idx_type i = 0; i < len; i++)
2315 {
2316 octave_value t2;
2317 bool dummy;
2318 std::string doc;
2319
2320 std::string name
2321 = read_binary_data (is, swap, fmt, "", dummy, t2, doc);
2322
2323 if (! is)
2324 error ("load: failed to load anonymous function handle");
2325
2326 m_local_vars[name] = t2;
2327 }
2328 }
2329
2330 if (is)
2331 return parse (ctmp2);
2332
2333 return false;
2334}
2335
2336bool
2337base_anonymous_fcn_handle::save_hdf5 (octave_hdf5_id loc_id,
2338 const char *name,
2339 bool save_as_floats)
2340{
2341#if defined (HAVE_HDF5)
2342
2343 bool retval = true;
2344
2345 octave_hdf5_id group_hid = -1;
2346#if defined (HAVE_HDF5_18)
2347 group_hid = H5Gcreate (loc_id, name, octave_H5P_DEFAULT, octave_H5P_DEFAULT,
2349#else
2350 group_hid = H5Gcreate (loc_id, name, 0);
2351#endif
2352 if (group_hid < 0)
2353 return false;
2354
2355 octave_hdf5_id space_hid, data_hid, type_hid;
2356 space_hid = data_hid = type_hid = -1;
2357
2358 // attach the type of the variable
2359 type_hid = H5Tcopy (H5T_C_S1);
2360 H5Tset_size (type_hid, m_name.length () + 1);
2361 if (type_hid < 0)
2362 {
2363 H5Gclose (group_hid);
2364 return false;
2365 }
2366
2367 OCTAVE_LOCAL_BUFFER (hsize_t, hdims, 2);
2368 hdims[0] = 0;
2369 hdims[1] = 0;
2370 space_hid = H5Screate_simple (0, hdims, nullptr);
2371 if (space_hid < 0)
2372 {
2373 H5Tclose (type_hid);
2374 H5Gclose (group_hid);
2375 return false;
2376 }
2377#if defined (HAVE_HDF5_18)
2378 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
2381#else
2382 data_hid = H5Dcreate (group_hid, "nm", type_hid, space_hid,
2384#endif
2385 if (data_hid < 0
2386 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
2387 octave_H5P_DEFAULT, m_name.c_str ()) < 0)
2388 {
2389 H5Sclose (space_hid);
2390 H5Tclose (type_hid);
2391 H5Gclose (group_hid);
2392 return false;
2393 }
2394 H5Dclose (data_hid);
2395
2396 std::ostringstream buf;
2397 print_raw (buf, true, 0);
2398 std::string stmp = buf.str ();
2399
2400 // attach the type of the variable
2401 H5Tset_size (type_hid, stmp.length () + 1);
2402 if (type_hid < 0)
2403 {
2404 H5Sclose (space_hid);
2405 H5Gclose (group_hid);
2406 return false;
2407 }
2408
2409#if defined (HAVE_HDF5_18)
2410 data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid,
2413#else
2414 data_hid = H5Dcreate (group_hid, "fcn", type_hid, space_hid,
2416#endif
2417 if (data_hid < 0
2418 || H5Dwrite (data_hid, type_hid, octave_H5S_ALL, octave_H5S_ALL,
2419 octave_H5P_DEFAULT, stmp.c_str ()) < 0)
2420 {
2421 H5Sclose (space_hid);
2422 H5Tclose (type_hid);
2423 H5Gclose (group_hid);
2424 return false;
2425 }
2426
2427 H5Dclose (data_hid);
2428
2429 std::size_t varlen = m_local_vars.size ();
2430
2431 if (varlen > 0)
2432 {
2433 octave_hdf5_id as_id = H5Screate (H5S_SCALAR);
2434
2435 if (as_id >= 0)
2436 {
2437 octave_hdf5_id a_id;
2438#if defined (HAVE_HDF5_18)
2439 a_id = H5Acreate (group_hid, "SYMBOL_TABLE", H5T_NATIVE_IDX, as_id,
2441
2442#else
2443 a_id = H5Acreate (group_hid, "SYMBOL_TABLE", H5T_NATIVE_IDX, as_id,
2445#endif
2446
2447 if (a_id >= 0)
2448 {
2449 retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0);
2450
2451 H5Aclose (a_id);
2452 }
2453 else
2454 retval = false;
2455
2456 H5Sclose (as_id);
2457 }
2458 else
2459 retval = false;
2460#if defined (HAVE_HDF5_18)
2461 data_hid = H5Gcreate (group_hid, "symbol table",
2464#else
2465 data_hid = H5Gcreate (group_hid, "symbol table", 0);
2466#endif
2467 if (data_hid < 0)
2468 {
2469 H5Sclose (space_hid);
2470 H5Tclose (type_hid);
2471 H5Gclose (group_hid);
2472 return false;
2473 }
2474
2475 for (const auto& nm_val : m_local_vars)
2476 {
2477 if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first,
2478 "", false, save_as_floats))
2479 break;
2480 }
2481
2482 H5Gclose (data_hid);
2483 }
2484
2485 H5Sclose (space_hid);
2486 H5Tclose (type_hid);
2487 H5Gclose (group_hid);
2488
2489 return retval;
2490
2491#else
2492
2493 octave_unused_parameter (loc_id);
2494 octave_unused_parameter (name);
2495 octave_unused_parameter (save_as_floats);
2496
2497 warn_save ("hdf5");
2498
2499 return false;
2500
2501#endif
2502}
2503
2504bool
2505base_anonymous_fcn_handle::load_hdf5 (octave_hdf5_id& group_hid,
2506 octave_hdf5_id& space_hid,
2507 octave_hdf5_id& type_hid)
2508{
2509#if defined (HAVE_HDF5)
2510
2511 bool success = true;
2512
2513#if defined (HAVE_HDF5_18)
2514 octave_hdf5_id data_hid = H5Dopen (group_hid, "fcn", octave_H5P_DEFAULT);
2515#else
2516 octave_hdf5_id data_hid = H5Dopen (group_hid, "fcn");
2517#endif
2518
2519 if (data_hid < 0)
2520 {
2521 H5Sclose (space_hid);
2522 H5Tclose (type_hid);
2523 H5Gclose (group_hid);
2524 return false;
2525 }
2526
2527 H5Tclose (type_hid);
2528 type_hid = H5Dget_type (data_hid);
2529 octave_hdf5_id type_class_hid = H5Tget_class (type_hid);
2530
2531 if (type_class_hid != H5T_STRING)
2532 {
2533 H5Sclose (space_hid);
2534 H5Tclose (type_hid);
2535 H5Dclose (data_hid);
2536 H5Gclose (group_hid);
2537 return false;
2538 }
2539
2540 H5Sclose (space_hid);
2541 space_hid = H5Dget_space (data_hid);
2542 hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
2543
2544 if (rank != 0)
2545 {
2546 H5Sclose (space_hid);
2547 H5Tclose (type_hid);
2548 H5Dclose (data_hid);
2549 H5Gclose (group_hid);
2550 return false;
2551 }
2552
2553 int slen = H5Tget_size (type_hid);
2554 if (slen < 0)
2555 {
2556 H5Sclose (space_hid);
2557 H5Tclose (type_hid);
2558 H5Dclose (data_hid);
2559 H5Gclose (group_hid);
2560 return false;
2561 }
2562
2563 OCTAVE_LOCAL_BUFFER (char, fcn_tmp, slen);
2564
2565 // create datatype for (null-terminated) string to read into:
2566 octave_hdf5_id st_id = H5Tcopy (H5T_C_S1);
2567 H5Tset_size (st_id, slen);
2568
2569 if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
2570 octave_H5P_DEFAULT, fcn_tmp)
2571 < 0)
2572 {
2573 H5Tclose (st_id);
2574 H5Sclose (space_hid);
2575 H5Tclose (type_hid);
2576 H5Dclose (data_hid);
2577 H5Gclose (group_hid);
2578 return false;
2579 }
2580 H5Tclose (st_id);
2581 H5Dclose (data_hid);
2582
2583 octave_idx_type len = 0;
2584
2585 // we have to pull some shenanigans here to make sure
2586 // HDF5 doesn't print out all sorts of error messages if we
2587 // call H5Aopen for a non-existing attribute
2588
2589 H5E_auto_t err_fcn;
2590 void *err_fcn_data;
2591
2592 // turn off error reporting temporarily, but save the error
2593 // reporting function:
2594#if defined (HAVE_HDF5_18)
2595 H5Eget_auto (octave_H5E_DEFAULT, &err_fcn, &err_fcn_data);
2596 H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
2597#else
2598 H5Eget_auto (&err_fcn, &err_fcn_data);
2599 H5Eset_auto (nullptr, nullptr);
2600#endif
2601
2602 octave_hdf5_id attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE");
2603
2604 if (attr_id >= 0)
2605 {
2606 if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0)
2607 success = false;
2608
2609 H5Aclose (attr_id);
2610 }
2611
2612 // restore error reporting:
2613#if defined (HAVE_HDF5_18)
2614 H5Eset_auto (octave_H5E_DEFAULT, err_fcn, err_fcn_data);
2615#else
2616 H5Eset_auto (err_fcn, err_fcn_data);
2617#endif
2618
2619 // Set up temporary scope to use for evaluating the text that
2620 // defines the anonymous function.
2621
2622 interpreter& interp = __get_interpreter__ ();
2623
2624 tree_evaluator& tw = interp.get_evaluator ();
2625
2626 tw.push_dummy_scope (fcn_tmp);
2627 unwind_action_safe restore_scope (&tree_evaluator::pop_scope, &tw);
2628
2629 if (len > 0 && success)
2630 {
2631 hsize_t num_obj = 0;
2632#if defined (HAVE_HDF5_18)
2633 data_hid = H5Gopen (group_hid, "symbol table", octave_H5P_DEFAULT);
2634#else
2635 data_hid = H5Gopen (group_hid, "symbol table");
2636#endif
2637 H5Gget_num_objs (data_hid, &num_obj);
2638 H5Gclose (data_hid);
2639
2640 if (num_obj != static_cast<hsize_t> (len))
2641 error ("load: failed to load anonymous function handle");
2642
2643 hdf5_callback_data dsub;
2644 int current_item = 0;
2645 for (octave_idx_type i = 0; i < len; i++)
2646 {
2647 if (hdf5_h5g_iterate (group_hid, "symbol table", &current_item,
2648 &dsub) <= 0)
2649 error ("load: failed to load anonymous function handle");
2650
2651 m_local_vars[dsub.name] = dsub.tc;
2652 }
2653 }
2654
2655 if (success)
2656 return parse (fcn_tmp);
2657
2658 return false;
2659
2660#else
2661
2662 octave_unused_parameter (group_hid);
2663 octave_unused_parameter (space_hid);
2664 octave_unused_parameter (type_hid);
2665
2666 return false;
2667
2668#endif
2669}
2670
2671void
2672base_anonymous_fcn_handle::print_raw (std::ostream& os, bool, int) const
2673{
2674 tree_print_code tpc (os);
2675
2677
2678 if (! f)
2679 error ("invalid anonymous function handle");
2680
2681 os << "@";
2682
2683 // The parameter list should always be valid for anonymous
2684 // functions, so we should always call accept for it, and it will
2685 // print the parens for us.
2686
2687 tree_parameter_list *p = f->parameter_list ();
2688
2689 if (p)
2690 p->accept (tpc);
2691
2692 os << " ";
2693
2694 tree_statement_list *b = f->body ();
2695
2696 panic_if (b->size () != 1);
2697
2698 tree_statement *s = b->front ();
2699
2700 if (! s)
2701 error ("invalid anonymous function handle");
2702
2704
2705 tree_expression *e = s->expression ();
2706
2707 if (! e)
2708 error ("invalid anonymous function handle");
2709
2710 tpc.print_fcn_handle_body (e);
2711}
2712
2713bool
2714base_anonymous_fcn_handle::parse (const std::string& fcn_text)
2715{
2716 // FIXME: If evaluation of the string gives us an anonymous function
2717 // handle object, then why extract the function and create a new
2718 // anonymous function object? Why not just attach the workspace
2719 // values to the object returned by eval_string? This code is also is
2720 // duplicated in read_mat5_binary_element in ls-mat5.cc.
2721
2722 interpreter& interp = __get_interpreter__ ();
2723
2724 // Set up temporary scope to use for evaluating the text that defines
2725 // the anonymous function so that we don't pick up values of random
2726 // variables that might be in the current scope.
2727
2728 tree_evaluator& tw = interp.get_evaluator ();
2729 tw.push_dummy_scope ("read_mat5_binary_element");
2730
2731 unwind_action act ([&tw] () { tw.pop_scope (); });
2732
2733 int parse_status;
2734 octave_value anonymous_fcn_hdl
2735 = interp.eval_string (fcn_text, true, parse_status);
2736
2737 if (parse_status != 0)
2738 return false;
2739
2740 octave_fcn_handle *fh = anonymous_fcn_hdl.fcn_handle_value ();
2741
2742 if (! fh)
2743 return false;
2744
2745 m_fcn = fh->fcn_val ();
2746
2747 octave_user_function *uf = m_fcn.user_function_value (true);
2748
2749 if (uf)
2750 {
2751 symbol_scope uf_scope = uf->scope ();
2752
2753 if (uf_scope)
2754 uf_scope.cache_name (m_name);
2755 }
2756
2757 return true;
2758}
2759
2760anonymous_fcn_handle::anonymous_fcn_handle (const octave_value& fcn,
2761 const stack_frame::local_vars_map& local_vars,
2762 const std::shared_ptr<stack_frame>& stack_context)
2763 : base_anonymous_fcn_handle (fcn, local_vars),
2764 m_stack_context (stack_context)
2765{
2766 if (m_stack_context)
2767 m_stack_context->mark_closure_context ();
2768}
2769
2771anonymous_fcn_handle::make_weak_anonymous_handle () const
2772{
2774 (new weak_anonymous_fcn_handle (*this)));
2775}
2776
2778anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
2779{
2781
2782 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
2783
2784 tw.push_stack_frame (oct_usr_fcn, m_local_vars, m_stack_context);
2785
2786 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
2787
2788 return oct_usr_fcn->execute (tw, nargout, args);
2789}
2790
2792anonymous_fcn_handle::workspace () const
2793{
2794 octave_scalar_map local_vars_map;
2795
2796 for (const auto& nm_val : m_local_vars)
2797 local_vars_map.assign (nm_val.first, nm_val.second);
2798
2799 // FIXME: it would be more convenient if stack_frame::workspace
2800 // returned a Cell object directly instead of a Cell in an
2801 // octave_value object.
2802
2803 Cell cell_frames;
2804
2805 if (m_stack_context)
2806 {
2807 octave_value ov_frames = m_stack_context->workspace ();
2808 cell_frames = ov_frames.cell_value ();
2809 }
2810
2811 octave_idx_type num_frames = cell_frames.numel ();
2812 // FIXME: It seems there should be a simple way to concatenate cells...
2813 Cell retval = Cell (num_frames+1, 1);
2814 retval(0) = m_local_vars;
2815 for (octave_idx_type i = 0; i < num_frames; i++)
2816 retval(i+1) = cell_frames(i);
2817
2818 return retval;
2819}
2820
2821bool
2822is_equal_to (const anonymous_fcn_handle& fh1,
2823 const anonymous_fcn_handle& fh2)
2824{
2825 if (fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2826 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2827 else
2828 return false;
2829}
2830
2832weak_anonymous_fcn_handle::call (int nargout, const octave_value_list& args)
2833{
2835
2836 octave_user_function *oct_usr_fcn = m_fcn.user_function_value ();
2837
2838 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
2839
2840 tw.push_stack_frame (oct_usr_fcn, m_local_vars, frames);
2841
2842 unwind_action act ([&tw] () { tw.pop_stack_frame (); });
2843
2844 return oct_usr_fcn->execute (tw, nargout, args);
2845}
2846
2848weak_anonymous_fcn_handle::workspace () const
2849{
2850 octave_scalar_map local_vars_map;
2851
2852 for (const auto& nm_val : m_local_vars)
2853 local_vars_map.assign (nm_val.first, nm_val.second);
2854
2855 // FIXME: it would be more convenient if stack_frame::workspace
2856 // returned a Cell object directly instead of a Cell in an
2857 // octave_value object.
2858
2859 std::shared_ptr<stack_frame> frames = m_stack_context.lock ();
2860
2861 Cell cell_frames;
2862
2863 if (frames)
2864 {
2865 octave_value ov_frames = frames->workspace ();
2866 cell_frames = ov_frames.cell_value ();
2867 }
2868
2869 octave_idx_type num_frames = cell_frames.numel ();
2870
2871 // FIXME: It seems there should be a simple way to concatenate
2872 // cells...
2873 Cell retval = Cell (num_frames+1, 1);
2874 retval(0) = m_local_vars;
2875 for (octave_idx_type i = 0; i < num_frames; i++)
2876 retval(i+1) = cell_frames(i);
2877
2878 return retval;
2879}
2880
2881bool
2882is_equal_to (const weak_anonymous_fcn_handle& fh1,
2883 const weak_anonymous_fcn_handle& fh2)
2884{
2885 if (fh1.m_name == fh2.m_name
2886 && fh1.m_fcn.is_defined () && fh2.m_fcn.is_defined ())
2887 return fh1.m_fcn.is_copy_of (fh2.m_fcn);
2888 else
2889 return false;
2890}
2891
2892OCTAVE_END_NAMESPACE(octave)
2893
2895 : octave_base_value (), m_rep (new octave::invalid_fcn_handle ())
2896{ }
2897
2899 : octave_base_value (), m_rep (new octave::internal_fcn_handle (fcn))
2900{ }
2901
2903 : octave_base_value (), m_rep (new octave::simple_fcn_handle (name))
2904{ }
2905
2907 const std::string& name)
2908 : octave_base_value (), m_rep (new octave::simple_fcn_handle (fcn, name))
2909{ }
2910
2911octave_fcn_handle::octave_fcn_handle (const std::string& class_nm,
2912 const std::string& meth_nm)
2913 : octave_base_value (),
2914 m_rep (new octave::class_simple_fcn_handle (class_nm, meth_nm))
2915{ }
2916
2918 const std::string& class_nm,
2919 const std::string& meth_nm)
2920 : octave_base_value (),
2921 m_rep (new octave::class_simple_fcn_handle (fcn, class_nm, meth_nm))
2922{ }
2923
2925 const octave_value& fcn,
2926 const std::string& class_nm,
2927 const std::string& meth_nm)
2928 : octave_base_value (),
2929 m_rep (new octave::class_simple_fcn_handle (obj, fcn, class_nm, meth_nm))
2930{ }
2931
2933 const std::string& name,
2934 const std::list<std::string>& parentage)
2935 : octave_base_value (),
2936 m_rep (new octave::scoped_fcn_handle (fcn, name, parentage))
2937{ }
2938
2940 const std::string& name,
2941 const std::shared_ptr<octave::stack_frame>& stack_context)
2942 : octave_base_value (),
2943 m_rep (new octave::nested_fcn_handle (fcn, name, stack_context))
2944{ }
2945
2947 const octave::stack_frame::local_vars_map& local_vars,
2948 const std::shared_ptr<octave::stack_frame>& stack_context)
2949 : octave_base_value (),
2950 m_rep (new octave::anonymous_fcn_handle (fcn, local_vars, stack_context))
2951{ }
2952
2953octave_fcn_handle::octave_fcn_handle (octave::base_fcn_handle *rep)
2954 : octave_base_value (), m_rep (rep)
2955{ }
2956
2958 : octave_base_value (fh)
2959{
2960 m_rep.reset (fh.m_rep->clone ());
2961}
2962
2965{
2966 static dim_vector dv (1, 1);
2967 return dv;
2968}
2969
2970bool
2972{
2973 return m_rep->save_ascii (os);
2974}
2975
2976bool
2978{
2979 std::shared_ptr<octave::base_fcn_handle> new_rep;
2980
2981 // Read enough to detect type then create new rep object and dispatch
2982 // to finish loading object.
2983
2984 std::streampos pos = is.tellg ();
2985
2986 std::string octaveroot = extract_keyword (is, "octaveroot", true);
2987 if (octaveroot.empty ())
2988 {
2989 is.seekg (pos);
2990 is.clear ();
2991 }
2992
2993 pos = is.tellg ();
2994
2995 std::string fpath = extract_keyword (is, "path", true);
2996 if (fpath.empty ())
2997 {
2998 is.seekg (pos);
2999 is.clear ();
3000 }
3001
3002 if (! (octaveroot.empty () || fpath.empty ()))
3003 {
3004 std::size_t len = octaveroot.size ();
3005 if (octaveroot == fpath.substr (0, len))
3006 fpath = octave::config::octave_exec_home () + fpath.substr (len);
3007 }
3008
3009 pos = is.tellg ();
3010
3011 std::string subtype = extract_keyword (is, "subtype", true);
3012 if (subtype.empty ())
3013 {
3014 is.seekg (pos);
3015 is.clear ();
3016
3017 // We have a legacy file that can contain either an anonymous
3018 // function or a simple function handle.
3019
3020 std::string name;
3021 is >> name;
3022
3023 if (name == anonymous)
3024 new_rep.reset (new octave::anonymous_fcn_handle ());
3025 else
3026 new_rep.reset (new octave::simple_fcn_handle (name, fpath, octaveroot));
3027 }
3028 else
3029 {
3030 // Load individual function handle types.
3031
3032 if (subtype == "simple")
3033 {
3034 std::string name;
3035 is >> name;
3036
3037 new_rep.reset (new octave::simple_fcn_handle (name, fpath,
3038 octaveroot));
3039 }
3040 else if (subtype == "scopedfunction")
3041 {
3042 std::string name;
3043 is >> name;
3044
3045 new_rep.reset (new octave::scoped_fcn_handle (name, fpath,
3046 octaveroot));
3047 }
3048 else if (subtype == "anonymous")
3049 new_rep.reset (new octave::anonymous_fcn_handle ());
3050 else if (subtype == "nested")
3051 {
3052 std::string name;
3053 is >> name;
3054
3055 new_rep.reset (new octave::nested_fcn_handle (name, fpath,
3056 octaveroot));
3057 }
3058 else if (subtype == "classsimple")
3059 {
3060 std::string name;
3061 is >> name;
3062
3063 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath,
3064 octaveroot));
3065 }
3066 }
3067
3068 if (! new_rep)
3069 return false;
3070
3071 if (! new_rep->load_ascii (is))
3072 return false;
3073
3074 m_rep = new_rep;
3075
3076 return true;
3077}
3078
3079bool
3080octave_fcn_handle::save_binary (std::ostream& os, bool save_as_floats)
3081{
3082 return m_rep->save_binary (os, save_as_floats);
3083}
3084
3085bool
3086octave_fcn_handle::load_binary (std::istream& is, bool swap,
3087 octave::mach_info::float_format fmt)
3088{
3089 // Read enough to detect type then create new rep object and dispatch
3090 // to finish loading object.
3091
3092 int32_t tmp;
3093 if (! is.read (reinterpret_cast<char *> (&tmp), 4))
3094 return false;
3095 if (swap)
3096 swap_bytes<4> (&tmp);
3097
3098 OCTAVE_LOCAL_BUFFER (char, ctmp1, tmp+1);
3099 // is.get (ctmp1, tmp+1, 0); caused is.eof () to be true though
3100 // effectively not reading over file end
3101 is.read (ctmp1, tmp);
3102 ctmp1[tmp] = 0;
3103 std::string name (ctmp1);
3104
3105 if (! is)
3106 return false;
3107
3108 std::shared_ptr<octave::base_fcn_handle> new_rep;
3109
3110 std::size_t anl = anonymous.length ();
3111
3112 if (name.length () >= anl && name.substr (0, anl) == anonymous)
3113 {
3114 // Even with extra info stored in the function name, anonymous
3115 // functions look the same. Note that NAME here may have the
3116 // number of local variables appended. We decode that inside the
3117 // load_binary function.
3118
3119 new_rep.reset (new octave::anonymous_fcn_handle (name));
3120 }
3121 else
3122 {
3123 // Unpack extra info stored with the function name and load
3124 // individual function handle types.
3125 // FIXME: is there a better way?
3126
3127 std::string octaveroot;
3128 std::string fpath;
3129 std::string subtype = "simple";
3130
3131 if (name.find_first_of ('\n') != std::string::npos)
3132 {
3133 std::size_t pos1 = name.find_first_of ('\n');
3134 std::size_t pos2 = name.find_first_of ('\n', pos1 + 1);
3135 octaveroot = name.substr (pos1 + 1, pos2 - pos1 - 1);
3136 fpath = name.substr (pos2 + 1);
3137 name = name.substr (0, pos1);
3138 }
3139
3140 std::size_t pos1 = name.find ('@');
3141 if (pos1 != std::string::npos)
3142 {
3143 if (name[pos1+1] == '<')
3144 {
3145 std::size_t pos2 = name.find ('>', pos1 + 2);
3146
3147 if (pos2 != std::string::npos)
3148 subtype = name.substr (pos1 + 2, pos2 - pos1 - 2);
3149 }
3150
3151 name = name.substr (0, pos1);
3152 }
3153
3154 // Anonymous should have been handled above so it is not in the
3155 // following list.
3156
3157 if (subtype == "simple")
3158 new_rep.reset (new octave::simple_fcn_handle (name, fpath, octaveroot));
3159 else if (subtype == "scopedfunction")
3160 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, octaveroot));
3161 else if (subtype == "nested")
3162 new_rep.reset (new octave::nested_fcn_handle (name, fpath, octaveroot));
3163 else if (subtype == "classsimple")
3164 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath,
3165 octaveroot));
3166 }
3167
3168 if (! new_rep)
3169 return false;
3170
3171 if (! new_rep->load_binary (is, swap, fmt))
3172 return false;
3173
3174 m_rep = new_rep;
3175
3176 return true;
3177}
3178
3179bool
3181 bool save_as_floats)
3182{
3183 return m_rep->save_hdf5 (loc_id, name, save_as_floats);
3184}
3185
3186bool
3187octave_fcn_handle::load_hdf5 (octave_hdf5_id loc_id, const char *name_arg)
3188{
3189#if defined (HAVE_HDF5)
3190
3191#if defined (HAVE_HDF5_18)
3192 octave_hdf5_id group_hid = H5Gopen (loc_id, name_arg, octave_H5P_DEFAULT);
3193#else
3194 octave_hdf5_id group_hid = H5Gopen (loc_id, name_arg);
3195#endif
3196 if (group_hid < 0)
3197 return false;
3198
3199#if defined (HAVE_HDF5_18)
3200 octave_hdf5_id data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
3201#else
3202 octave_hdf5_id data_hid = H5Dopen (group_hid, "nm");
3203#endif
3204
3205 if (data_hid < 0)
3206 {
3207 H5Gclose (group_hid);
3208 return false;
3209 }
3210
3211 octave_hdf5_id type_hid = H5Dget_type (data_hid);
3212 octave_hdf5_id type_class_hid = H5Tget_class (type_hid);
3213
3214 if (type_class_hid != H5T_STRING)
3215 {
3216 H5Tclose (type_hid);
3217 H5Dclose (data_hid);
3218 H5Gclose (group_hid);
3219 return false;
3220 }
3221
3222 octave_hdf5_id space_hid = H5Dget_space (data_hid);
3223 hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
3224
3225 if (rank != 0)
3226 {
3227 H5Sclose (space_hid);
3228 H5Tclose (type_hid);
3229 H5Dclose (data_hid);
3230 H5Gclose (group_hid);
3231 return false;
3232 }
3233
3234 int slen = H5Tget_size (type_hid);
3235 if (slen < 0)
3236 {
3237 H5Sclose (space_hid);
3238 H5Tclose (type_hid);
3239 H5Dclose (data_hid);
3240 H5Gclose (group_hid);
3241 return false;
3242 }
3243
3244 OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
3245
3246 // create datatype for (null-terminated) string to read into:
3247 octave_hdf5_id st_id = H5Tcopy (H5T_C_S1);
3248 H5Tset_size (st_id, slen);
3249
3250 if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
3251 octave_H5P_DEFAULT, nm_tmp)
3252 < 0)
3253 {
3254 H5Tclose (st_id);
3255 H5Sclose (space_hid);
3256 H5Tclose (type_hid);
3257 H5Dclose (data_hid);
3258 H5Gclose (group_hid);
3259 return false;
3260 }
3261 H5Tclose (st_id);
3262 H5Dclose (data_hid);
3263
3264 std::string name (nm_tmp);
3265
3266 std::shared_ptr<octave::base_fcn_handle> new_rep;
3267
3268 if (name == anonymous)
3269 {
3270 // Even with extra info stored in the function name, anonymous
3271 // functions look the same.
3272
3273 new_rep.reset (new octave::anonymous_fcn_handle ());
3274 }
3275 else
3276 {
3277 // Unpack extra info stored with the function name and load
3278 // individual function handle types.
3279 // FIXME: is there a better way?
3280
3281 std::string octaveroot;
3282 std::string fpath;
3283 std::string subtype = "simple";
3284
3285 if (name.find_first_of ('\n') != std::string::npos)
3286 {
3287 std::size_t pos1 = name.find_first_of ('\n');
3288 std::size_t pos2 = name.find_first_of ('\n', pos1 + 1);
3289 octaveroot = name.substr (pos1 + 1, pos2 - pos1 - 1);
3290 fpath = name.substr (pos2 + 1);
3291 name = name.substr (0, pos1);
3292 }
3293
3294 std::size_t pos1 = name.find ('@');
3295 if (pos1 != std::string::npos)
3296 {
3297 if (name[pos1+1] == '<')
3298 {
3299 std::size_t pos2 = name.find ('>', pos1 + 2);
3300
3301 if (pos2 != std::string::npos)
3302 subtype = name.substr (pos1 + 2, pos2 - pos1 - 2);
3303 }
3304
3305 name = name.substr (0, pos1);
3306 }
3307
3308 // Anonymous should have been handled above so it is not in the
3309 // following list.
3310
3311 if (subtype == "simple")
3312 new_rep.reset (new octave::simple_fcn_handle (name, fpath, octaveroot));
3313 else if (subtype == "scopedfunction")
3314 new_rep.reset (new octave::scoped_fcn_handle (name, fpath, octaveroot));
3315 else if (subtype == "nested")
3316 new_rep.reset (new octave::nested_fcn_handle (name, fpath, octaveroot));
3317 else if (subtype == "classsimple")
3318 new_rep.reset (new octave::class_simple_fcn_handle (name, fpath,
3319 octaveroot));
3320 }
3321
3322 bool status = false;
3323
3324 if (new_rep && new_rep->load_hdf5 (group_hid, space_hid, type_hid))
3325 {
3326 m_rep = new_rep;
3327 status = true;
3328 }
3329
3330 // FIXME: manage these with an unwind_action object?
3331
3332 H5Tclose (type_hid);
3333 H5Sclose (space_hid);
3334 H5Gclose (group_hid);
3335
3336 return status;
3337
3338#else
3339
3340 octave_unused_parameter (loc_id);
3341 octave_unused_parameter (name_arg);
3342
3343 warn_load ("hdf5");
3344
3345 return false;
3346
3347#endif
3348}
3349
3350/*
3351%!test <*33857>
3352%! a = 2;
3353%! f = @(x) a + x;
3354%! g = @(x) 2 * x;
3355%! hm = @version;
3356%! hdld = @svd;
3357%! hbi = @log2;
3358%! f2 = f;
3359%! g2 = g;
3360%! hm2 = hm;
3361%! hdld2 = hdld;
3362%! hbi2 = hbi;
3363%! modes = {"-text", "-binary"};
3364%! if (isfield (__octave_config_info__, "HAVE_HDF5")
3365%! && __octave_config_info__ ("HAVE_HDF5"))
3366%! modes(end+1) = "-hdf5";
3367%! endif
3368%! for i = 1:numel (modes)
3369%! mode = modes{i};
3370%! nm = tempname ();
3371%! unwind_protect
3372%! f2 (1);
3373%! save (mode, nm, "f2", "g2", "hm2", "hdld2", "hbi2");
3374%! clear f2 g2 hm2 hdld2 hbi2
3375%! load (nm);
3376%! assert (f (2), f2 (2));
3377%! assert (g (2), g2 (2));
3378%! assert (g (3), g2 (3));
3379%! unlink (nm);
3380%! save (mode, nm, "f2", "g2", "hm2", "hdld2", "hbi2");
3381%! unwind_protect_cleanup
3382%! unlink (nm);
3383%! end_unwind_protect
3384%! endfor
3385*/
3386
3387/*
3388%!function fcn_handle_save_recurse (n, mode, nm, f2, g2, hm2, hdld2, hbi2)
3389%! if (n == 0)
3390%! save (mode, nm, "f2", "g2", "hm2", "hdld2", "hbi2");
3391%! else
3392%! fcn_handle_save_recurse (n - 1, mode, nm, f2, g2, hm2, hdld2, hbi2);
3393%! endif
3394%!endfunction
3395%!function [f2, g2, hm2, hdld2, hbi2] = fcn_handle_load_recurse (n, nm)
3396%! if (n == 0)
3397%! load (nm);
3398%! else
3399%! [f2, g2, hm2, hdld2, hbi2] = fcn_handle_load_recurse (n - 1, nm);
3400%! endif
3401%!endfunction
3402
3403%!test <*35876>
3404%! a = 2;
3405%! f = @(x) a + x;
3406%! g = @(x) 2 * x;
3407%! hm = @version;
3408%! hdld = @svd;
3409%! hbi = @log2;
3410%! f2 = f;
3411%! g2 = g;
3412%! hm2 = hm;
3413%! hdld2 = hdld;
3414%! hbi2 = hbi;
3415%! modes = {"-text", "-binary"};
3416%! if (isfield (__octave_config_info__, "HAVE_HDF5")
3417%! && __octave_config_info__ ("HAVE_HDF5"))
3418%! modes(end+1) = "-hdf5";
3419%! endif
3420%! for i = 1:numel (modes)
3421%! mode = modes{i};
3422%! nm = tempname ();
3423%! unwind_protect
3424%! fcn_handle_save_recurse (2, mode, nm, f2, g2, hm2, hdld2, hbi2);
3425%! clear f2 g2 hm2 hdld2 hbi2
3426%! [f2, f2, hm2, hdld2, hbi2] = fcn_handle_load_recurse (2, nm);
3427%! load (nm);
3428%! assert (f (2), f2 (2));
3429%! assert (g (2), g2 (2));
3430%! assert (g (3), g2 (3));
3431%! unlink (nm);
3432%! fcn_handle_save_recurse (2, mode, nm, f2, g2, hm2, hdld2, hbi2);
3433%! unwind_protect_cleanup
3434%! unlink (nm);
3435%! end_unwind_protect
3436%! endfor
3437*/
3438
3439void
3440octave_fcn_handle::print (std::ostream& os, bool pr_as_read_syntax)
3441{
3442 print_raw (os, pr_as_read_syntax);
3443 newline (os);
3444}
3445
3446void
3447octave_fcn_handle::print_raw (std::ostream& os, bool pr_as_read_syntax) const
3448{
3449 m_rep->print_raw (os, pr_as_read_syntax, current_print_indent_level ());
3450}
3451
3452bool
3454{
3455 // FIXME: Maybe there is a better way? Possibly by using typeid or
3456 // typeindex?
3457
3458 // Don't include invalid_fcn_handle in the list of types to compare.
3459 // Consider them to be like NaN values so comparisons between any two
3460 // invalid handles are always false.
3461
3462 if (fh1.is_internal () && fh2.is_internal ())
3463 return is_equal_to (*dynamic_cast<octave::internal_fcn_handle *> (fh1.get_rep ()),
3464 *dynamic_cast<octave::internal_fcn_handle *> (fh2.get_rep ()));
3465 else if (fh1.is_simple () && fh2.is_simple ())
3466 return is_equal_to (*dynamic_cast<octave::simple_fcn_handle *> (fh1.get_rep ()),
3467 *dynamic_cast<octave::simple_fcn_handle *> (fh2.get_rep ()));
3468 else if (fh1.is_scoped () && fh2.is_scoped ())
3469 return is_equal_to (*dynamic_cast<octave::scoped_fcn_handle *> (fh1.get_rep ()),
3470 *dynamic_cast<octave::scoped_fcn_handle *> (fh2.get_rep ()));
3471 else if (fh1.is_nested () && fh2.is_nested ())
3472 return is_equal_to (*dynamic_cast<octave::nested_fcn_handle *> (fh1.get_rep ()),
3473 *dynamic_cast<octave::nested_fcn_handle *> (fh2.get_rep ()));
3474 else if (fh1.is_class_simple () && fh2.is_class_simple ())
3475 return is_equal_to (*dynamic_cast<octave::class_simple_fcn_handle *> (fh1.get_rep ()),
3476 *dynamic_cast<octave::class_simple_fcn_handle *> (fh2.get_rep ()));
3477 else if (fh1.is_anonymous () && fh2.is_anonymous ())
3478 return is_equal_to (*dynamic_cast<octave::anonymous_fcn_handle *> (fh1.get_rep ()),
3479 *dynamic_cast<octave::anonymous_fcn_handle *> (fh2.get_rep ()));
3480 else
3481 return false;
3482}
3483
3485
3486DEFUN (functions, args, ,
3487 doc: /* -*- texinfo -*-
3488@deftypefn {} {@var{s} =} functions (@var{fcn_handle})
3489Return a structure containing information about the function handle
3490@var{fcn_handle}.
3491
3492The structure @var{s} always contains these three fields:
3493
3494@table @asis
3495@item function
3496The function name. For an anonymous function (no name) this will be the
3497actual function definition.
3498
3499@item type
3500Type of the function.
3501
3502@table @asis
3503@item anonymous
3504The function is anonymous.
3505
3506@item private
3507The function is private.
3508
3509@item overloaded
3510The function overloads an existing function.
3511
3512@item simple
3513The function is a built-in or m-file function.
3514
3515@item subfunction
3516The function is a subfunction within an m-file.
3517@end table
3518
3519@item nested
3520The function is nested.
3521
3522@item file
3523The m-file that will be called to perform the function. This field is empty
3524for anonymous and built-in functions.
3525@end table
3526
3527In addition, some function types may return more information in additional
3528fields.
3529
3530@strong{Warning:} @code{functions} is provided for debugging purposes only.
3531Its behavior may change in the future and programs should not depend on any
3532particular output format.
3533
3534@seealso{func2str, str2func}
3535@end deftypefn */)
3536{
3537 if (args.length () != 1)
3538 print_usage ();
3539
3540 octave_fcn_handle *fh = args(0).xfcn_handle_value ("functions: FCN_HANDLE argument must be a function handle object");
3541
3542 return ovl (fh->info ());
3543}
3544
3545DEFUN (func2str, args, ,
3546 doc: /* -*- texinfo -*-
3547@deftypefn {} {@var{str} =} func2str (@var{fcn_handle})
3548Return a string containing the name of the function referenced by the function
3549handle @var{fcn_handle}.
3550@seealso{str2func, functions}
3551@end deftypefn */)
3552{
3553 if (args.length () != 1)
3554 print_usage ();
3555
3556 octave_fcn_handle *fh = args(0).xfcn_handle_value ("func2str: FCN_HANDLE argument must be a function handle object");
3557
3558 if (! fh)
3559 error ("func2str: FCN_HANDLE must be a valid function handle");
3560
3561 octave_value retval;
3562
3563 std::string fh_nm = fh->fcn_name ();
3564
3565 if (fh->is_anonymous ())
3566 {
3567 std::ostringstream buf;
3568
3569 fh->print_raw (buf);
3570
3571 retval = buf.str ();
3572 }
3573 else
3574 retval = fh_nm;
3575
3576 return retval;
3577}
3578
3579DEFMETHOD (str2func, interp, args, ,
3580 doc: /* -*- texinfo -*-
3581@deftypefn {} {@var{hfcn} =} str2func (@var{str})
3582Return a function handle constructed from the string @var{str}.
3583
3584The input may be the name of a function such as @qcode{"sin"} or a string
3585defining a function such as @qcode{"@@(x) sin (x + pi)"}.
3586
3587Programming Note: In most cases it will be better to use anonymous function
3588syntax and let the Octave parser create the function handle rather than use
3589@code{str2func}. For example:
3590
3591@example
3592@group
3593hfcn = @@sin ;
3594hfcn = @@(x) sin (x + pi) ;
3595@end group
3596@end example
3597
3598@seealso{func2str, functions}
3599@end deftypefn */)
3600{
3601 int nargin = args.length ();
3602
3603 if (nargin < 1 || nargin > 2)
3604 print_usage ();
3605
3606 std::string nm = args(0).xstring_value ("str2func: FCN_NAME must be a string");
3607
3608 if (nm.empty ())
3609 error ("str2func: invalid function name");
3610
3611 if (nm[0] == '@')
3612 {
3613 // Unlike the anonymous_fcn_handle::parse method, don't set up
3614 // temporary scope to use for evaluating the text that defines
3615 // the anonymous function. Here we want
3616 //
3617 // str2func ("@(args) expr")
3618 //
3619 // to behave the same as if
3620 //
3621 // @(args) expr
3622 //
3623 // were evaluated in the current scope.
3624
3625 int parse_status;
3626 octave_value afh = interp.eval_string (nm, true, parse_status);
3627
3628 if (parse_status == 0)
3629 return afh;
3630 }
3631 else
3632 {
3633 if (nargin == 2)
3634 warning_with_id ("Octave:str2func-global-argument",
3635 "str2func: second argument ignored");
3636
3637 tree_evaluator& tw = interp.get_evaluator ();
3638
3639 return tw.make_fcn_handle (nm);
3640 }
3641
3642 return ovl ();
3643}
3644
3645/*
3646%!test
3647%! f = str2func ("<");
3648%! assert (class (f), "function_handle");
3649%! assert (func2str (f), "lt");
3650%! assert (f (1, 2), true);
3651%! assert (f (2, 1), false);
3652
3653%!test
3654%! f = str2func ("@(x) sin (x)");
3655%! assert (func2str (f), "@(x) sin (x)");
3656%! assert (f (0:3), sin (0:3));
3657
3658%!error <FCN_NAME must be a string> str2func ({"sin"})
3659*/
3660
3661/*
3662%!function y = __testrecursionfcn (f, x, n)
3663%! if (nargin < 3)
3664%! n = 0;
3665%! endif
3666%! if (n > 2)
3667%! y = f (x);
3668%! else
3669%! n++;
3670%! y = __testrecursionfcn (@(x) f (2*x), x, n);
3671%! endif
3672%!endfunction
3673%!
3674%!assert (__testrecursionfcn (@(x) x, 1), 8)
3675*/
3676
3677DEFUN (is_function_handle, args, ,
3678 doc: /* -*- texinfo -*-
3679@deftypefn {} {@var{tf} =} is_function_handle (@var{x})
3680Return true if @var{x} is a function handle.
3681@seealso{isa, typeinfo, class, functions}
3682@end deftypefn */)
3683{
3684 if (args.length () != 1)
3685 print_usage ();
3686
3687 return ovl (args(0).is_function_handle ());
3688}
3689
3690/*
3691%!shared fh
3692%! fh = @(x) x;
3693
3694%!assert (is_function_handle (fh))
3695%!assert (! is_function_handle ({fh}))
3696%!assert (! is_function_handle (1))
3697
3698%!error is_function_handle ()
3699%!error is_function_handle (1, 2)
3700*/
3701
3702/*
3703%!test
3704%! f = @(t) eval ('2*t');
3705%! assert (f (21), 42);
3706*/
3707
3708/*
3709%!test <*58389>
3710%! s = "x";
3711%! a.(s) = [e, pi];
3712%! f = @(x) a.(s)(x);
3713%! assert (f(1), e);
3714%! assert (f(2), pi);
3715%! assert (f([2,1]), [pi, e]);
3716*/
3717
3718/*
3719%!function r = __f (g, i)
3720%! r = g(i);
3721%!endfunction
3722%!test
3723%! x = [1,2;3,4];
3724%! assert (__f (@(i) x(:,i), 1), [1;3]);
3725*/
3726
3727OCTAVE_END_NAMESPACE(octave)
void swap_bytes< 4 >(void *ptr)
Definition byte-swap.h:63
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
Definition Cell.h:41
virtual bool save_binary(std::ostream &os, bool save_as_floats)
virtual bool is_scoped() const
virtual bool save_hdf5(octave_hdf5_id loc_id, const char *name, bool save_as_floats)
virtual bool is_class_simple() const
virtual octave_function * function_value(bool=false)
std::string m_name
void unimplemented(const char *op, const char *fmt) const
std::string file() const
virtual bool print_as_scalar() const
virtual bool save_ascii(std::ostream &os)
void warn_save(const char *file_type) const
virtual bool load_binary(std::istream &is, bool swap, mach_info::float_format fmt)
virtual bool is_simple() const
virtual octave_user_function * user_function_value(bool=false)
virtual bool load_hdf5(octave_hdf5_id &group_hid, octave_hdf5_id &space_hid, octave_hdf5_id &type_hid)
virtual octave_scalar_map info()
virtual octave_value_list call(int nargout, const octave_value_list &args)=0
void warn_load(const char *file_type) const
virtual base_fcn_handle * clone() const =0
virtual octave_value make_weak_anonymous_handle() const
virtual octave_value workspace() const
virtual bool is_nested() const
virtual bool load_ascii(std::istream &is)
virtual octave_value fcn_val()
octave_value_list subsref(const std::string &type, const std::list< octave_value_list > &idx, int nargout)
octave_value convert_to_str_internal(bool pad, bool force, char type) const
virtual octave_value make_weak_nested_handle() const
virtual bool is_anonymous() const
virtual std::string type() const =0
std::string m_file
virtual void print_raw(std::ostream &, bool, int) const
virtual bool is_internal() const
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:90
octave_value varval(const std::string &name) const
octave_value_list eval_string(const std::string &eval_str, bool silent, int &parse_status, int nargout)
tree_evaluator & get_evaluator()
octave_value_list feval(const char *name, const octave_value_list &args=octave_value_list(), int nargout=0)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
symbol_table & get_symbol_table()
int current_print_indent_level() const
Definition ov-base.h:945
void newline(std::ostream &os) const
Definition ov-base.cc:1377
void warn_load(const char *type) const
Definition ov-base.cc:1152
virtual octave_function * function_value(bool silent=false)
Definition ov-base.cc:935
Array< std::string > cellstr_value() const
Definition ov-cell.cc:600
bool load_ascii(std::istream &is)
Definition ov-cell.cc:765
bool iscellstr() const
Definition ov-cell.cc:395
bool load_binary(std::istream &is, bool swap, octave::mach_info::float_format fmt)
Definition ov-cell.cc:897
static const std::string anonymous
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
bool load_hdf5(octave_hdf5_id loc_id, const char *name)
bool is_nested() const
bool load_ascii(std::istream &is)
octave_scalar_map info()
bool is_class_simple() const
dim_vector dims() const
octave_value fcn_val()
bool is_internal() const
std::string fcn_name() const
bool save_binary(std::ostream &os, bool save_as_floats)
bool is_anonymous() const
bool save_ascii(std::ostream &os)
bool save_hdf5(octave_hdf5_id loc_id, const char *name, bool save_as_floats)
bool is_simple() const
bool is_scoped() const
bool load_binary(std::istream &is, bool swap, octave::mach_info::float_format fmt)
void print(std::ostream &os, bool pr_as_read_syntax=false)
std::string dispatch_class() const
Definition ov-fcn.h:150
bool is_class_method(const std::string &cname="") const
Definition ov-fcn.h:117
void lock()
Definition ov-fcn.h:176
virtual std::string fcn_file_name() const
Definition ov-fcn.h:74
void setfield(const std::string &key, const octave_value &val)
Definition oct-map.cc:190
octave::symbol_scope scope()
Definition ov-usr-fcn.h:97
std::string fcn_file_name() const
Definition ov-usr-fcn.h:112
octave_value_list execute(octave::tree_evaluator &tw, int nargout=0, const octave_value_list &args=octave_value_list())
octave_user_function * user_function_value(bool=false)
Definition ov-usr-fcn.h:230
octave_value find_subfunction(const std::string &subfuns) const
octave_idx_type length() const
Definition ovl.h:111
octave_value_list & prepend(const octave_value &val)
Definition ovl.cc:80
bool is_classdef_object() const
Definition ov.h:655
octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition ov.h:476
octave_function * function_value(bool silent=false) const
Cell cell_value() const
bool is_classdef_meta() const
Definition ov.h:652
void lock()
Definition ov.h:1446
bool is_defined() const
Definition ov.h:592
bool save_binary(std::ostream &os, bool save_as_floats)
Definition ov.h:1385
bool save_ascii(std::ostream &os)
Definition ov.h:1381
bool is_function() const
Definition ov.h:777
octave_fcn_handle * fcn_handle_value(bool silent=false) const
bool is_package() const
Definition ov.h:661
octave_idx_type length() const
bool is_cs_list() const
Definition ov.h:670
octave_user_function * user_function_value(bool silent=false) const
std::map< std::string, octave_value > local_vars_map
void cache_name(const std::string &name)
Definition symscope.h:591
octave_value find_private_function(const std::string &dir_name, const std::string &name)
Definition symtab.cc:101
octave_value find_function(const std::string &name, const symbol_scope &search_scope=symbol_scope::invalid())
Definition symtab.cc:254
octave_value find_user_function(const std::string &name)
Definition symtab.cc:293
void pop_stack_frame()
Definition pt-eval.cc:2529
void set_dispatch_class(const std::string &class_name)
Definition pt-eval.cc:2655
void push_dummy_scope(const std::string &name)
Definition pt-eval.cc:2746
void pop_scope()
Definition pt-eval.cc:2754
octave_value make_fcn_handle(const std::string &nm)
Definition pt-eval.cc:1624
void push_stack_frame(const symbol_scope &scope)
Definition pt-eval.cc:2496
void accept(tree_walker &tw)
Definition pt-misc.h:110
tree_expression * expression()
Definition pt-stmt.h:104
bool is_expression() const
Definition pt-stmt.h:78
const octave_hdf5_id octave_H5P_DEFAULT
const octave_hdf5_id octave_H5E_DEFAULT
const octave_hdf5_id octave_H5S_ALL
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
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
void warning_with_id(const char *id, const char *fmt,...)
Definition error.cc:1093
void error(const char *fmt,...)
Definition error.cc:1003
tree_evaluator & __get_evaluator__()
symbol_table & __get_symbol_table__()
interpreter & __get_interpreter__()
F77_RET_T const F77_DBLE const F77_DBLE * f
octave_hdf5_err hdf5_h5g_iterate(octave_hdf5_id loc_id, const char *name, int *idx, void *operator_data)
Definition ls-hdf5.cc:1064
bool add_hdf5_data(octave_hdf5_id loc_id, const octave_value &tc, const std::string &name, const std::string &doc, bool mark_global, bool save_as_floats)
Definition ls-hdf5.cc:1411
std::string read_binary_data(std::istream &is, bool swap, octave::mach_info::float_format fmt, const std::string &filename, bool &global, octave_value &tc, std::string &doc)
bool save_binary_data(std::ostream &os, const octave_value &tc, const std::string &name, const std::string &doc, bool mark_global, bool save_as_floats)
std::string read_text_data(std::istream &is, const std::string &filename, bool &global, octave_value &tc, octave_idx_type count, const bool do_name_validation)
std::string extract_keyword(std::istream &is, const char *keyword, const bool next_only)
bool save_text_data(std::ostream &os, const octave_value &val_arg, const std::string &name, bool mark_global, int precision)
int64_t octave_hdf5_id
#define H5T_NATIVE_IDX
Definition oct-hdf5.h:42
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition oct-locbuf.h:44
#define DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(t, n, c)
Definition ov-base.h:246
bool is_equal_to(const anonymous_fcn_handle &fh1, const anonymous_fcn_handle &fh2)
bool is_equal_to(const octave_fcn_handle &fh1, const octave_fcn_handle &fh2)
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
#define panic_if(cond)
Definition panic.h:57
#define panic_unless(cond)
Definition panic.h:59
void octave_print_internal(std::ostream &os, const float_display_format &fmt, bool d, bool pr_as_read_syntax)
octave_value tc
Definition ls-hdf5.h:121
std::string name
Definition ls-hdf5.h:115
F77_RET_T len
Definition xerbla.cc:61