GNU Octave 11.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
ov-classdef.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2012-2026 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include <algorithm>
31#include <iomanip>
32
33#include "cdef-class.h"
34#include "cdef-method.h"
35#include "cdef-package.h"
36#include "cdef-property.h"
37#include "cdef-utils.h"
38#include "defun.h"
39#include "errwarn.h"
40#include "interpreter-private.h"
41#include "load-path.h"
42#include "ls-oct-text.h"
43#include "ov-classdef.h"
44#include "ov-fcn-handle.h"
45#include "ov-typeinfo.h"
46#include "ov-usr-fcn.h"
47#include "parse.h"
48#include "pr-output.h"
49#include "pt-eval.h"
50#include "pt-misc.h"
51#include "oct-lvalue.h"
52
53std::vector<std::tuple<octave_map, uint32_t, bool>>
54octave_classdef::saveobj (std::vector<bool>& is_new)
55{
56 octave::cdef_class cls = m_object.get_class ();
57 octave::cdef_method meth = cls.find_method ("saveobj");
58
59 // Vector with tuples consisting of a map with property values, the object id,
60 // and an indicator whether the object has a custom return type
61 std::vector<std::tuple<octave_map, uint32_t, bool>> m (numel ());
62
63 octave::load_save_system& lss = octave::__get_load_save_system__ ();
64
65 // The saveobj method needs to be called separately for each element of
66 // N-D arrays.
67 for (octave_idx_type n = 0; n < numel (); n++)
68 {
69 octave_value retval;
70
71 octave_value_list ovl_idx;
72 std::list<octave_value_list> idx_tmp;
73 ovl_idx(0) = n+1;
74 idx_tmp.push_back (ovl_idx);
75 octave_value elem = (subsref ("(", idx_tmp, 1))(0);
76
77 // Check if the element is already in the cache of objects that are
78 // stored in this file.
79 // Use the address of the cdef_object_rep as the key.
80 const octave::cdef_object co = elem.classdef_object_value ()->get_object_ref ();
81 bool is_new_elem;
82 std::get<uint32_t> (m[n])
83 = lss.get_mcos_object_cache_id (co.get_rep (), is_new_elem);
84 is_new[n] = is_new_elem;
85
86 if (is_new[n])
87 {
88 // Default behavior of 'loadobj' is to return this object's
89 // map_value, including all protected, private and hidden properties.
90 if (! meth.ok () || meth.is_static ())
91 // Default behaviour of saving is triggered if saveobj is static
92 std::get<octave_map> (m[n])
93 = elem.classdef_object_value ()->map_value (false, true);
94 else
95 {
96 retval = (meth.execute (elem.classdef_object_value ()->get_object_ref (),
97 octave_value_list (), 1))(0);
98
99 // default: no custom return type
100 std::get<bool> (m[n]) = false;
101
102 if (! retval.is_defined ())
103 {
104 warning ("save: saveobj method does not return a value");
105 return m;
106 }
107 else if (! retval.is_classdef_object ()
108 || retval.class_name () != class_name ())
109 {
110 // If retval is not an object of the matching class, we put
111 // the value in a map and set the 'custom_saveobj_ret_type'
112 // flag, which has to be encoded in the file metadata.
113 // It's the caller's responsibility to check the flag.
114 std::get<octave_map> (m[n]).setfield ("any", retval);
115 std::get<bool> (m[n]) = true;
116 }
117
118 else if (retval.is_classdef_object ())
119 std::get<octave_map> (m[n])
120 = retval.classdef_object_value ()->map_value (false, true);
121 else
122 std::get<octave_map> (m[n]) = retval.map_value ();
123 }
124 }
125 }
126
127 return m;
128}
129
130void
131octave_classdef::loadobj (std::vector<std::tuple<octave_map, uint32_t, bool>>& m,
132 dim_vector& dv)
133{
134 octave::cdef_object scalar_obj = m_object.copy ();
135 octave::cdef_class cls = m_object.get_class ();
136 octave::cdef_method meth = cls.find_method ("loadobj");
137
138 octave::load_save_system& lss = octave::__get_load_save_system__ ();
139
140 // The loadobj method needs to be called separately for each element of
141 // N-D arrays.
142 for (octave_idx_type n = 0; static_cast<size_t> (n) < m.size (); n++)
143 {
144 octave_value ovc;
145
146 // Check if object is already loaded in cache
147 uint32_t id = std::get<uint32_t> (m[n]);
148 bool in_obj_cache = lss.is_mcos_object_cache_entry (id);
149 if (in_obj_cache)
150 ovc = lss.get_mcos_object_cache_entry (id);
151
152 octave_map& prop_map = std::get<octave_map> (m[n]);
153
154 if (! in_obj_cache || prop_map.nfields () > 0)
155 {
156 // Default behaviour of loading is triggered if loadobj is not static
157 if (meth.ok () && meth.is_static () && prop_map.nfields () > 0)
158 {
159 octave_value ov;
160 if (std::get<bool> (m[n]))
161 {
162 octave_value any = prop_map.contents ("any").elem (0);
163 ov = (meth.execute (octave_value_list (any), 1))(0);
164 }
165 else
166 {
167 // create object from saved properties
168 octave::cdef_object new_object;
169 if (in_obj_cache)
170 new_object = ovc.classdef_object_value ()->m_object;
171 else
172 new_object = scalar_obj.copy ();
173
174 bool props_changed = false;
175 string_vector fnames = prop_map.fieldnames ();
176 string_vector sv = map_keys ();
177 for (octave_idx_type i = 0; i < prop_map.nfields (); i++)
178 {
180 for (j = 0; j < sv.numel (); j++)
181 {
182 if (sv[j] == fnames(i))
183 {
184 new_object.set_property (0, sv[j], prop_map.contents (fnames(i)).xelem (0));
185 break;
186 }
187 }
188 if (j == sv.numel ())
189 {
190 // properties have been renamed or deleted
191 props_changed = true;
192 break;
193 }
194 }
195
196 if (props_changed)
197 // attempting to create the object failed
198 // call loadobj with struct
199 ov = (meth.execute (octave_value_list (prop_map), 1))(0);
200 else
201 {
202 if (! in_obj_cache)
203 ovc = octave::to_ov (new_object);
204
205 // pass object to loadobj
206 ov = (meth.execute (octave_value_list (ovc), 1))(0);
207 }
208 }
209
210 if (! ov.is_defined ())
211 {
212 warning ("load: loadobj method does not return a value");
213 return;
214 }
215
216 // FIXME: A loadobj method can return any type. If the return
217 // type is not a classdef object of the correct class,
218 // then the loaded object must be replaced by whatever the
219 // return type and contents are.
220 if (! ov.is_classdef_object ())
221 {
222 std::string type = ov.type_name ();
223 warning ("load: loadobj method does not return correct type "
224 "'%s'. This is currently not supported.",
225 type.c_str ());
226 return;
227 }
228 else if (ov.class_name () != class_name ())
229 {
230 std::string class_nm = ov.class_name ();
231 warning ("load: loadobj method does not return classdef object "
232 "of correct class '%s'. This is currently not supported.",
233 class_nm.c_str ());
234 return;
235 }
236
237 if (in_obj_cache)
238 // Copy the results from the loadobj methods to the object in
239 // the cache.
240 ovc.classdef_object_value ()->m_object = ov.classdef_object_value ()->m_object;
241 else
242 ovc = ov;
243 }
244 else
245 {
246 if (std::get<bool> (m[n]))
247 {
248 // If saveobj is overloaded by this classdef and it returned
249 // anything other than a classdef object of the correct
250 // class, then a variable named 'any' is meant to be passed
251 // to loadobj, but if loadobj is not overloaded, it should
252 // not fill in any property 'any' in the loaded object.
253 if (! prop_map.isfield ("any") || prop_map.numel () != 1)
254 {
255 warning ("load: expected scalar value for custom type when loading object");
256 return;
257 }
258
259 // FIXME: What should be done here?
260 octave_value any_val = (prop_map.getfield ("any"))(0);
261 std::string type = any_val.type_name ();
262 if (type != type_name ())
263 {
264 warning ("load: cannot restore value of object that was saved as a different type (%s)",
265 type.c_str ());
266 return;
267 }
268
269 std::string cls_nm = any_val.class_name ();
270 if (cls_nm != class_name ())
271 {
272 warning ("load: cannot restore value of object that was saved as a different class (%s)",
273 cls_nm.c_str ());
274 return;
275 }
276
277 // If the value in the "any" field has the correct type and
278 // class, we can load it like it were saved "normally".
279 // FIXME: Can this ever happen?
280 prop_map = any_val.classdef_object_value ()->map_value (false, true);
281 }
282
283 octave::cdef_object new_object;
284 if (in_obj_cache)
285 new_object = ovc.classdef_object_value ()->m_object;
286 else
287 new_object = scalar_obj.copy ();
288
289 string_vector fnames = prop_map.fieldnames ();
290 string_vector sv = map_keys ();
291 for (octave_idx_type i = 0; i < prop_map.nfields (); i++)
292 for (octave_idx_type j = 0; j < sv.numel (); j++)
293 if (sv[j] == fnames(i))
294 {
295 new_object.set_property (0, sv[j], prop_map.contents (fnames(i)).xelem (0));
296 break;
297 }
298
299 if (! in_obj_cache)
300 ovc = octave::to_ov (new_object);
301 }
302
303 lss.set_mcos_object_cache_entry (id, ovc);
304 }
305
306 if (m.size () == 1)
307 m_object = ovc.classdef_object_value ()->m_object;
308 else
309 {
310 octave_value_list ovl_idx;
311 std::list<octave_value_list> idx_tmp;
312 ovl_idx(0) = n+1;
313 idx_tmp.push_back (ovl_idx);
314 octave_value tmp = subsasgn ("(", idx_tmp, ovc);
315
316 // FIXME: Is this assignment only needed for value classes?
317 m_object = tmp.classdef_object_value ()->m_object;
318 }
319 }
320
321 // Reshape to the correct dimensions.
322 if (dv != dims ())
323 {
324 octave_value ov_reshaped = reshape (dv);
325 m_object = ov_reshaped.classdef_object_value ()->m_object;
326 }
327}
328
329bool
331{
332 os << "# classname: " << class_name () << "\n";
333
334 const dim_vector dv = m_object.dims ();
335 os << "# ndims: " << dv.ndims () << "\n";
336
337 for (int i = 0; i < dv.ndims (); i++)
338 os << ' ' << dv(i);
339 os << "\n";
340
341 std::vector<bool> is_new (numel ());
342 std::vector<std::tuple<octave_map, uint32_t, bool>> m
343 = saveobj (is_new);
344
345 for (octave_idx_type n = 0; static_cast<size_t> (n) < m.size (); n++)
346 {
347 os << "# id: " << std::get<uint32_t> (m[n]) << "\n";
348
349 // Check if this is a reference to an already existing object
350 if (! is_new[n])
351 {
352 os << "# length: 0\n";
353 continue;
354 }
355
356 octave_idx_type nf = std::get<octave_map> (m[n]).nfields ();
357
358 os << "# length: " << nf << "\n";
359
360 os << "# metadata: ";
361 if (std::get<bool> (m[n]))
362 os << "saveobj_defined";
363 os << "\n";
364
365 string_vector keys = std::get<octave_map> (m[n]).fieldnames ();
366
367 for (octave_idx_type i = 0; i < nf; i++)
368 {
369 std::string key = keys(i);
370
371 // querying values from an octave_map returns 'Cell' objects
372 octave_value val = (std::get<octave_map> (m[n]).contents (key))(0);
373
374 bool b = save_text_data (os, val, key, false, 0);
375
376 if (! b)
377 return ! os.fail ();
378 }
379 }
380
381 return true;
382}
383
384bool
386{
388 dim_vector dv (1, 1);
389
390 if (extract_keyword (is, "ndims", len, true))
391 {
392 int mdims = std::max (static_cast<int> (len), 2);
393 dv.resize (mdims);
394 for (int i = 0; i < mdims; i++)
395 is >> dv(i);
396 }
397 else
398 error ("load: failed to extract keyword 'ndims' for classdef object");
399
400 // vector for each element in the array containing:
401 // * a map with the values of the class properties
402 // * a unique identifier of the object in the file
403 // * an indicator whether the object has a custom return type
404 std::vector<std::tuple<octave_map, uint32_t, bool>> m (dv.numel ());
405
406 for (octave_idx_type i = 0; i < dv.numel (); i++)
407 {
408 if (! extract_keyword (is, "id", std::get<uint32_t> (m[i]), true))
409 error ("load: failed to extract keyword 'id' for classdef object");
410
411 if (! extract_keyword (is, "length", len, true))
412 error ("load: failed to extract keyword 'length' for classdef object");
413
414 if (len < 0)
415 error ("load: failed to extract number of properties for classdef object");
416
417 if (len == 0)
418 continue;
419
420 std::string metadata;
421 if (! extract_keyword (is, "metadata", metadata, true))
422 error ("load: failed to extract keyword 'metadata' for classdef object");
423
424 size_t pos = metadata.find ("saveobj_defined");
425 std::get<bool> (m[i]) = (pos != std::string::npos);
426
427 for (octave_idx_type j = 0; j < len; j++)
428 {
429 octave_value t2;
430 bool dummy;
431
432 std::string nm = read_text_data (is, "", dummy, t2, j, false);
433
434 if (! is)
435 break;
436
437 // Set the field in the octave_map
438 std::get<octave_map> (m[i]).setfield (nm, t2);
439 }
440
441 if (! is)
442 error ("load: failed to load classdef object");
443 }
444
445 loadobj (m, dv);
446
447 return true;
448}
449
450static bool
451in_class_method (const octave::cdef_class& cls)
452{
453 octave::cdef_class ctx = octave::get_class_context ();
454
455 return (ctx.ok () && octave::is_superclass (ctx, cls));
456}
457
458int octave_classdef::s_t_id (-1);
459
460const std::string octave_classdef::s_t_name ("object");
461
462void
463octave_classdef::register_type (octave::type_info& ti)
464{
465 s_t_id = ti.register_type (octave_classdef::s_t_name, "<unknown>",
467}
468
470octave_classdef::subsref (const std::string& type,
471 const std::list<octave_value_list>& idx,
472 int nargout)
473{
474 std::size_t skip = 0;
475 octave_value_list retval;
476
477 octave::cdef_class cls = m_object.get_class ();
478
479 if (! in_class_method (cls) && ! called_from_builtin ())
480 {
481 octave::cdef_method meth = cls.find_method ("subsref");
482
483 if (meth.ok ())
484 {
486
487 args(1) = make_idx_args (type, idx, "subsref");
488
489 m_count++;
490 args(0) = octave_value (this);
491
492 if (nargout <= 0)
493 {
494 // If the last index type is not '()', the final value of nargout is
495 // unknown. Try to get its value
496 if (type.back () != '(')
497 {
498 // See if method numArgumentsFromSubscript is defined
499 octave::cdef_method meth_nargout
500 = cls.find_method ("numArgumentsFromSubscript");
501
502 if (meth_nargout.ok ())
503 {
504 octave_value_list args_nargout (3);
505
506 args_nargout(0) = args(0);
507 args_nargout(1) = args(1);
508 // FIXME: Third argument should be one of the possible values of
509 // the matlab.mixin.util.IndexingContext enumeration class.
510 args_nargout(2) = octave_value (Matrix ());
511 retval = meth_nargout.execute (args_nargout, 1, true,
512 "numArgumentsFromSubscript");
513
514 nargout = retval(0).strict_int_value
515 ("subsref: return value of 'numArgumentsFromSubscript' must be integer");
516 }
517 else
518 {
519 // Method numArgumentsFromSubscript undefined. Attempt to set up
520 // a proper value for nargout at least in the simple case where the
521 // cs-list-type expression - i.e., {} or ().x, is the leading one.
522
523 // Set up a proper nargout for the subsref call by calling numel.
525 int nout;
526 if (type[0] != '.')
527 tmp = idx.front ();
528
529 nout = xnumel (tmp);
530 // Take nout as nargout for subsref, unless the index expression
531 // is a whole sentence starting with the form id.member and id is
532 // one element (in that case, nargout remains 0).
533 if (type[0] != '.' || nout != 1 || nargout < 0)
534 nargout = nout;
535 }
536 }
537 else if (nargout < 0)
538 nargout = 1;
539 }
540
541 retval = meth.execute (args, nargout, true, "subsref");
542
543 // Since we're handling subsref, if the list has more than one element
544 // and the caller to subsref accepts more that one output, return
545 // the elements as a comma-separated list so that we can pass it to the
546 // evaluator
547 if (retval.length () > 1 && (nargout < 0 || nargout > 1))
548 {
549 if (nargout <= 0 || nargout >= retval.length ())
550 // Take the whole list
551 retval = octave_value (retval);
552 else
553 // Take nargout elements of the list
554 retval = octave_value (retval.slice (0, nargout));
555 }
556
557 return retval;
558 }
559 }
560
561 // At this point, the default subsref mechanism must be used.
562
563 retval = m_object.subsref (type, idx, nargout, skip, octave::cdef_class ());
564
565 if (type.length () > skip && idx.size () > skip)
566 retval = retval(0).next_subsref (nargout, type, idx, skip);
567
568 return retval;
569}
570
572octave_classdef::subsref (const std::string& type,
573 const std::list<octave_value_list>& idx,
574 bool auto_add)
575{
576 std::size_t skip = 0;
577 octave_value_list retval;
578
579 // This variant of subsref is used to create temporary values when doing
580 // assignment with multi-level indexing. AFAIK this is only used for internal
581 // purpose (not sure we should even implement this).
582
583 octave::cdef_class cls = m_object.get_class ();
584
585 if (! in_class_method (cls))
586 {
587 octave::cdef_method meth = cls.find_method ("subsref");
588
589 if (meth.ok ())
590 {
592
593 args(1) = make_idx_args (type, idx, "subsref");
594
595 m_count++;
596 args(0) = octave_value (this);
597
598 retval = meth.execute (args, 1, true, "subsref");
599
600 return retval.length () > 0 ? retval(0) : octave_value ();
601 }
602 }
603
604 retval = m_object.subsref (type, idx, 1, skip,
605 octave::cdef_class (), auto_add);
606
607 if (type.length () > skip && idx.size () > skip)
608 retval = retval(0).next_subsref (1, type, idx, skip);
609
610 return retval.length () > 0 ? retval(0) : octave_value ();
611}
612
614octave_classdef::subsasgn (const std::string& type,
615 const std::list<octave_value_list>& idx,
616 const octave_value& rhs)
617{
618 octave_value retval;
619
620 octave::cdef_class cls = m_object.get_class ();
621
622 if (! in_class_method (cls) && ! called_from_builtin ())
623 {
624 octave::cdef_method meth = cls.find_method ("subsasgn");
625
626 if (meth.ok ())
627 {
629
630 args(1) = make_idx_args (type, idx, "subsasgn");
631
632 m_count++;
633 args(0) = octave_value (this);
634 args(2) = rhs;
635
636 octave_value_list retlist;
637
638 retlist = meth.execute (args, 1, true, "subsasgn");
639
640 if (retlist.empty ())
641 error ("overloaded method 'subsasgn' did not return any value");
642
643 retval = retlist(0);
644 }
645 }
646
647 if (! retval.is_defined ())
648 retval = m_object.subsasgn (type, idx, rhs);
649
650 return retval;
651}
652
654octave_classdef::undef_subsasgn (const std::string& type,
655 const std::list<octave_value_list>& idx,
656 const octave_value& rhs)
657{
658 if (type.length () == 1 && type[0] == '(')
659 {
660 m_object = m_object.make_array ();
661
662 return subsasgn (type, idx, rhs);
663 }
664 else
665 return octave_base_value::undef_subsasgn (type, idx, rhs);
666
667 return octave_value ();
668}
669
670Matrix
672{
673 octave::cdef_class cls = m_object.get_class ();
674
675 if (! in_class_method (cls) && ! called_from_builtin ())
676 {
677 octave::cdef_method meth = cls.find_method ("size");
678
679 if (meth.ok ())
680 {
681 m_count++;
682 octave_value_list args (1, octave_value (this));
683
684 octave_value_list lv = meth.execute (args, 1, true, "size");
685 if (lv.length () <= 0
686 || ! lv(0).is_matrix_type () || ! lv(0).dims ().isvector ())
687 error ("%s.size: invalid return value", class_name ().c_str ());
688
689 return lv(0).matrix_value ();
690 }
691 }
692
693 return octave_base_value::size ();
694}
695
698{
699 octave_idx_type retval = -1;
700
701 // FIXME: This method is only used in subsref and subsasgn operations, to find
702 // out the number of elements in the cs-list corresponding to the subsref
703 // output or the subsasgn lvalue.
704 // This method currently calls the classdef's numel method to do its task, but
705 // this is incompatible with Matlab. Matlab calls numArgumentsFromSubscript
706 // for that purpose. We cannot call numArgumentsFromSubscript here because that
707 // method needs all the information about the indices of the subsref/subsasgn
708 // operation (possibly multiple levels of indexing of different types)
709
710 octave::cdef_class cls = m_object.get_class ();
711
712 if (! in_class_method (cls) && ! called_from_builtin ())
713 {
714 octave::cdef_method meth = cls.find_method ("numel");
715
716 if (meth.ok ())
717 {
718 octave_value_list args (idx.length () + 1, octave_value ());
719
720 m_count++;
721 args(0) = octave_value (this);
722
723 for (octave_idx_type i = 0; i < idx.length (); i++)
724 args(i+1) = idx(i);
725
726 // Temporarily set lvalue list of current statement to NULL, to avoid
727 // using that list for the execution of the method "numel"
728 octave::interpreter& interp = octave::__get_interpreter__ ();
729 octave::tree_evaluator& tw = interp.get_evaluator();
730
731 octave::unwind_action act ([&tw] (const std::list<octave::octave_lvalue> *lvl)
732 {
733 tw.set_lvalue_list (lvl);
734 }, tw.lvalue_list ());
735 tw.set_lvalue_list (nullptr);
736
737 octave_value_list lv = meth.execute (args, 1, true, "numel");
738 if (lv.length () != 1 || ! lv(0).is_scalar_type ())
739 error ("@%s/numel: invalid return value", cls.get_name ().c_str ());
740
741 retval = lv(0).idx_type_value (true);
742
743 return retval;
744 }
745 }
746
747 retval = octave_base_value::xnumel (idx);
748
749 return retval;
750}
751
754{
755 octave_value retval;
756
757 octave::cdef_class cls = m_object.get_class ();
758
759 if (! in_class_method (cls) && ! called_from_builtin ())
760 {
761 octave::cdef_method meth = cls.find_method ("reshape");
762
763 if (meth.ok ())
764 {
766
767 args(0) = octave::to_ov (m_object.clone ());
768 args(1) = new_dims.as_array ();
769
770 octave_value_list retlist;
771
772 retlist = meth.execute (args, 1, true, "reshape");
773
774 if (retlist.empty ())
775 error ("overloaded method 'reshape' did not return any value");
776
777 retval = retlist(0);
778 }
779 }
780
781 if (! retval.is_defined ())
782 retval = m_object.reshape (new_dims);
783
784 return retval;
785}
786
787void
788octave_classdef::print (std::ostream& os, bool)
789{
790 print_raw (os);
791}
792
793void
794octave_classdef::print_raw (std::ostream& os, bool) const
795{
796 octave::cdef_class cls = m_object.get_class ();
797
798 if (cls.ok ())
799 {
800 bool is_array = m_object.is_array ();
801
803
804 indent (os);
805
806 dim_vector dv = dims ();
807 if (dv.ndims () > 4)
808 os << dv.ndims () << "-D";
809 else
810 os << dims().str();
811
812 os << " " << class_name () << " object";
813 if (is_array)
814 os << " array";
815 os << " with properties:";
816 newline (os);
817 if (! Vcompact_format)
818 newline (os);
819
821
822 std::map<octave::property_key, octave::cdef_property> property_map
823 = cls.get_property_map ();
824
825 std::size_t max_len = 0;
826 for (const auto& pname_prop : property_map)
827 {
828 // FIXME: this loop duplicates a significant portion of the
829 // loop below and the loop in Fproperties.
830
831 const octave::cdef_property& prop = pname_prop.second;
832
833 const std::string nm = prop.get_name ();
834
835 octave_value acc = prop.get ("GetAccess");
836
837 if (! acc.is_string () || acc.string_value () != "public")
838 continue;
839
840 octave_value hid = prop.get ("Hidden");
841
842 if (hid.bool_value ())
843 continue;
844
845 std::size_t sz = nm.size ();
846
847 if (sz > max_len)
848 max_len = sz;
849 }
850
851 for (auto& pname_prop : property_map)
852 {
853 const octave::cdef_property& prop = pname_prop.second;
854
855 const std::string nm = prop.get_name ();
856
857 octave_value acc = prop.get ("GetAccess");
858
859 if (! acc.is_string () || acc.string_value () != "public")
860 continue;
861
862 octave_value hid = prop.get ("Hidden");
863
864 if (hid.bool_value ())
865 continue;
866
867 indent (os);
868
869 if (is_array)
870 os << " " << nm;
871 else
872 {
873 os << std::setw (max_len+2) << nm << ": ";
874
875 octave_value val = prop.get_value (m_object, false);
876
877 val.short_disp (os);
878 }
879
880 newline (os);
881 }
882
885 }
886}
887
888bool
889octave_classdef::is_instance_of (const std::string& cls_name) const
890{
891 octave::cdef_class cls = octave::lookup_class (cls_name, false, false);
892
893 if (cls.ok ())
894 return is_superclass (cls, m_object.get_class ());
895
896 return false;
897}
898
900octave_classdef::superclass_ref (const std::string& meth,
901 const std::string& cls)
902{
903 return octave_value (new octave_classdef_superclass_ref (meth, cls));
904}
905
907octave_classdef::metaclass_query (const std::string& cls)
908{
909 return octave::to_ov (octave::lookup_class (cls));
910}
911
912bool
913octave_classdef_meta::is_classdef_method (const std::string& cname) const
914{
915 bool retval = false;
916
917 if (m_object.is_method ())
918 {
919 if (cname.empty ())
920 retval = true;
921 else
922 {
923 octave::cdef_method meth (m_object);
924
925 return meth.is_defined_in_class (cname);
926 }
927 }
928
929 return retval;
930}
931
932bool
933octave_classdef_meta::is_classdef_constructor (const std::string& cname) const
934{
935 bool retval = false;
936
937 if (m_object.is_class ())
938 {
939 if (cname.empty ())
940 retval = true;
941 else
942 {
943 octave::cdef_class cls (m_object);
944
945 if (cls.get_name () == cname)
946 retval = true;
947 }
948 }
949 else if (m_object.is_method ())
950 {
951 octave::cdef_method meth (m_object);
952
953 if (meth.is_constructor ())
954 {
955 std::string meth_name = meth.get_name ();
956
957 // Only consider METH to be a constructor if the dispatch
958 // class CNAME is the same as or derived from the class of
959 // METH.
960
961 if (cname == meth_name)
962 retval = true;
963 else
964 {
965 octave::cdef_class meth_cls = octave::lookup_class (meth_name, false, false);
966 octave::cdef_class dispatch_cls = octave::lookup_class (cname, false, false);
967
968 retval = octave::is_superclass (meth_cls, dispatch_cls);
969 }
970 }
971 }
972
973 return retval;
974}
975
976std::string
977octave_classdef_meta::doc_string (const std::string& meth_name) const
978{
979 if (m_object.is_class ())
980 {
981 octave::cdef_class cls (m_object);
982
983 if (meth_name.empty ())
984 return cls.doc_string ();
985
986 octave::cdef_method cdef_meth = cls.find_method (meth_name);
987
988 if (cdef_meth.ok ())
989 return cdef_meth.get_doc_string ();
990 }
991
992 return "";
993}
994
995std::string
997{
998 if (m_object.is_class ())
999 {
1000 octave::cdef_class cls (m_object);
1001
1002 return cls.file_name ();
1003 }
1004
1005 return "";
1006}
1007
1010 int nargout,
1011 const octave_value_list& idx)
1012{
1013 octave_value_list retval;
1014
1015 std::string meth_name;
1016 bool in_constructor;
1017 octave::cdef_class ctx;
1018
1019 ctx = octave::get_class_context (meth_name, in_constructor);
1020
1021 if (! ctx.ok ())
1022 error ("superclass calls can only occur in methods or constructors");
1023
1024 std::string mname = m_method_name;
1025 std::string cname = m_class_name;
1026
1027 // CLS is the superclass. The lookup_class function handles
1028 // pkg.class names.
1029
1030 octave::cdef_class cls = octave::lookup_class (cname);
1031
1032 if (in_constructor)
1033 {
1034 if (! is_direct_superclass (cls, ctx))
1035 error ("'%s' is not a direct superclass of '%s'",
1036 cname.c_str (), ctx.get_name ().c_str ());
1037
1038 if (! is_constructed_object (tw, mname))
1039 error ("cannot call superclass constructor with variable '%s'",
1040 mname.c_str ());
1041
1042 octave_value sym = tw.varval (mname);
1043
1044 cls.run_constructor (octave::to_cdef_ref (sym), idx);
1045
1046 retval(0) = sym;
1047 }
1048 else
1049 {
1050 std::size_t pos = mname.find ('.');
1051
1052 octave::cdef_object obj;
1053
1054 if (pos != std::string::npos)
1055 {
1056 // We are looking at obj.meth.
1057
1058 std::string oname = m_method_name.substr (0, pos);
1059 mname = mname.substr (pos + 1);
1060
1061 octave_value tval = tw.varval (oname);
1062
1063 // FIXME: Can we only call superclass methods on the current
1064 // object? If so, and we are looking at something like
1065 //
1066 // function meth (obj, ...)
1067 // obj.meth@superclass (...)
1068 //
1069 // Do we need to verify that the object that was passed to
1070 // meth is the same as the object we find when looking up
1071 // obj in the expression obj.meth? If so, what is the right
1072 // way to perform that check?
1073
1074 if (tval.is_classdef_object ())
1075 {
1076 octave_classdef *cdobj = tval.classdef_object_value ();
1077
1078 obj = cdobj->get_object ();
1079 }
1080 }
1081
1082 if (mname != meth_name)
1083 error ("method name mismatch ('%s' != '%s')",
1084 mname.c_str (), meth_name.c_str ());
1085
1086 if (! is_strict_superclass (cls, ctx))
1087 error ("'%s' is not a superclass of '%s'",
1088 cname.c_str (), ctx.get_name ().c_str ());
1089
1090 // I see 2 possible implementations here:
1091 // 1) use cdef_object::subsref with a different class
1092 // context; this avoids duplicating code, but
1093 // assumes the object is always the first argument
1094 // 2) lookup the method manually and call
1095 // cdef_method::execute; this duplicates part of
1096 // logic in cdef_object::subsref, but avoid the
1097 // assumption of 1)
1098 // Not being sure about the assumption of 1), I
1099 // go with option 2) for the time being.
1100
1101 octave::cdef_method meth = cls.find_method (meth_name, false);
1102
1103 if (! meth.ok ())
1104 error ("no method '%s' found in superclass '%s'",
1105 meth_name.c_str (), cname.c_str ());
1106
1107 retval = (obj.ok ()
1108 ? meth.execute (obj, idx, nargout, true, meth_name)
1109 : meth.execute (idx, nargout, true, meth_name));
1110 }
1111
1112 return retval;
1113}
1114
1115bool
1116octave_classdef_superclass_ref::is_constructed_object (octave::tree_evaluator& tw,
1117 const std::string& nm)
1118{
1119 octave_function *of = tw.current_function ();
1120
1121 if (of->is_classdef_constructor ())
1122 {
1124
1125 if (uf)
1126 {
1127 octave::tree_parameter_list *ret_list = uf->return_list ();
1128
1129 if (ret_list && ret_list->size () == 1)
1130 return (ret_list->front ()->name () == nm);
1131 }
1132 }
1133
1134 return false;
1135}
1136
1138
1139DEFUN (__meta_get_package__, args, ,
1140 doc: /* -*- texinfo -*-
1141@deftypefn {} {@var{pkg} =} __meta_get_package__ (@var{pkg_name})
1142Undocumented internal function.
1143@end deftypefn */)
1144{
1145 if (args.length () != 1)
1146 print_usage ();
1147
1148 std::string cname = args(0).xstring_value ("PKG_NAME must be a string");
1149
1150 return to_ov (lookup_package (cname));
1151}
1152
1153DEFUN (metaclass, args, ,
1154 doc: /* -*- texinfo -*-
1155@deftypefn {} {@var{metaclass_obj} =} metaclass (obj)
1156Return the meta.class object corresponding to the class of @var{obj}.
1157@end deftypefn */)
1158{
1159 if (args.length () != 1)
1160 print_usage ();
1161
1162 cdef_object obj = to_cdef (args(0));
1163
1164 return to_ov (obj.get_class ());
1165}
1166
1167// FIXME: What about dynamic properties if obj is a scalar, or the
1168// properties of the class of obj if obj is an array? Probably there
1169// should be a function to do this job so that the DEFUN is just a
1170// simple wrapper.
1171
1172DEFUN (properties, args, nargout,
1173 doc: /* -*- texinfo -*-
1174@deftypefn {} {} properties (@var{obj})
1175@deftypefnx {} {} properties (@var{class_name})
1176@deftypefnx {} {@var{proplist} =} properties (@dots{})
1177Display or return the public properties for the classdef object @var{obj} or
1178the named class @var{class_name}.
1179
1180If an output value is requested, return the list of property names in a cell
1181array.
1182
1183Programming Note: Property names are returned if the @code{GetAccess} attribute
1184is public and if the @code{Hidden} attribute is false.
1185@seealso{methods}
1186@end deftypefn */)
1187{
1188 if (args.length () != 1)
1189 print_usage ();
1190
1191 octave_value arg = args(0);
1192
1193 std::string class_name;
1194
1195 if (arg.isobject ())
1196 class_name = arg.class_name ();
1197 else if (arg.is_string ())
1198 class_name = arg.string_value ();
1199 else
1200 err_wrong_type_arg ("properties", arg);
1201
1202 cdef_class cls;
1203
1204 cls = lookup_class (class_name, false, true);
1205
1206 if (! cls.ok ())
1207 error ("invalid class: %s", class_name.c_str ());
1208
1209 std::map<octave::property_key, cdef_property> property_map =
1210 cls.get_property_map ();
1211
1212 std::list<std::string> property_names;
1213
1214 for (const auto& pname_prop : property_map)
1215 {
1216 // FIXME: this loop duplicates a significant portion of the loops
1217 // in octave_classdef::print_raw.
1218 const cdef_property& prop = pname_prop.second;
1219
1220 octave_value acc = prop.get ("GetAccess");
1221
1222 if (! acc.is_string () || acc.string_value () != "public")
1223 continue;
1224
1225 octave_value hid = prop.get ("Hidden");
1226
1227 if (hid.bool_value ())
1228 continue;
1229
1230 property_names.push_back (pname_prop.second.get_name ());
1231 }
1232
1233 if (nargout > 0)
1234 return octave_value (Cell (string_vector (property_names)));
1235
1236 octave_stdout << "properties for class " << class_name << ":\n\n";
1237
1238 for (const auto& nm : property_names)
1239 octave_stdout << " " << nm << "\n";
1240
1241 octave_stdout << std::endl;
1242
1243 return octave_value ();
1244}
1245
1246/*
1247%!assert (properties ("inputParser"),
1248%! {"CaseSensitive"; "FunctionName"; "KeepUnmatched"; "PartialMatching";
1249%! "StructExpand"; "Parameters"; "Results"; "Unmatched";
1250%! "UsingDefaults"});
1251*/
1252
1253// FIXME: Need to implement the -full option.
1254
1255DEFMETHOD (__methods__, interp, args, ,
1256 doc: /* -*- texinfo -*-
1257@deftypefn {} {[@var{mtds}, @var{found}] =} __methods__ (@var{obj})
1258@deftypefnx {} {[@var{mtds}, @var{found}] =} __methods__ ("classname")
1259Implement @code{methods} for Octave class objects and classnames.
1260@seealso{methods}
1261@end deftypefn */)
1262{
1263 // Input validation has already been done in methods.m.
1264 octave_value arg = args(0);
1265
1266 std::string class_name;
1267
1268 if (arg.isobject ())
1269 class_name = arg.class_name ();
1270 else if (arg.is_string ())
1271 class_name = arg.string_value ();
1272 else
1273 err_wrong_type_arg ("__methods__", arg);
1274
1275 string_vector sv;
1276 bool found = false;
1277
1278 cdef_class cls = lookup_class (class_name, false, true);
1279
1280 if (cls.ok ())
1281 {
1282 // Find methods for classdef objects.
1283 std::map<std::string, cdef_method> method_map
1284 = cls.get_method_map (false, true);
1285
1286 std::list<std::string> method_names;
1287
1288 for (const auto& nm_mthd : method_map)
1289 {
1290 const cdef_method& method = nm_mthd.second;
1291
1292 octave_value acc = method.get ("Access");
1293
1294 if (! acc.is_string () || acc.string_value () != "public")
1295 continue;
1296
1297 octave_value hid = method.get ("Hidden");
1298
1299 if (hid.bool_value ())
1300 continue;
1301
1302 method_names.push_back (nm_mthd.first);
1303 }
1304
1305 sv = string_vector (method_names);
1306 found = true;
1307 }
1308 else
1309 {
1310 // Find methods for legacy @CLASS objects.
1311 load_path& lp = interp.get_load_path ();
1312
1313 sv = string_vector (lp.methods (class_name));
1314 found = ! sv.empty ();
1315 }
1316
1317 return ovl (Cell (sv), found);
1318}
1319
1320/*
1321// BIST tests are in file methods.m
1322%!assert (1)
1323*/
1324
1325OCTAVE_END_NAMESPACE(octave)
bool isvector(const dim_vector &dim)
bool is_superclass(const cdef_class &clsa, const cdef_class &clsb, bool allow_equal, int max_depth)
bool is_strict_superclass(const cdef_class &clsa, const cdef_class &clsb)
cdef_object to_cdef(const octave_value &val)
cdef_class lookup_class(const std::string &name, bool error_if_not_found, bool load_if_not_found)
Definition cdef-utils.cc:80
octave_value to_ov(const cdef_object &obj)
cdef_package lookup_package(const std::string &name, bool error_if_not_found, bool load_if_not_found)
bool is_direct_superclass(const cdef_class &clsa, const cdef_class &clsb)
T & elem(octave_idx_type n)
Size of the specified dimension.
Definition Array-base.h:585
Definition Cell.h:41
std::map< property_key, cdef_property > get_property_map(int mode=property_normal)
Definition cdef-class.h:311
std::map< std::string, cdef_method > get_method_map(bool only_inherited=false, bool include_ctor=false)
Definition cdef-class.h:293
cdef_class get_class() const
bool ok() const
octave_value get(const std::string &pname) const
Vector representing the dimensions (size) of an Array.
Definition dim-vector.h:92
std::string str(char sep='x') const
Definition dim-vector.cc:58
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition dim-vector.h:341
void resize(int n, int fill_value=0)
Definition dim-vector.h:278
Array< octave_idx_type > as_array() const
octave_idx_type ndims() const
Number of dimensions.
Definition dim-vector.h:263
std::list< std::string > methods(const std::string &class_name, const std::string &pack_name="")
Definition load-path.h:91
virtual octave_idx_type numel() const
Definition ov-base.h:401
virtual bool is_scalar_type() const
Definition ov-base.h:519
void increment_indent_level() const
Definition ov-base.h:939
void indent(std::ostream &os) const
Definition ov-base.cc:1358
virtual Matrix size()
Definition ov-base.cc:220
void newline(std::ostream &os) const
Definition ov-base.cc:1377
static void register_type()
Definition ov-base.cc:101
octave::refcount< octave_idx_type > m_count
Definition ov-base.h:958
virtual octave_idx_type xnumel(const octave_value_list &)
Definition ov-base.cc:230
void decrement_indent_level() const
Definition ov-base.h:942
virtual octave_user_function * user_function_value(bool silent=false)
Definition ov-base.cc:944
virtual octave_value undef_subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
Definition ov-base.cc:360
friend class octave_value
Definition ov-base.h:278
virtual octave_value any(int=0) const
Definition ov-base.cc:425
virtual bool is_matrix_type() const
Definition ov-base.h:521
bool is_classdef_method(const std::string &cname="") const
std::string file_name() const
bool is_classdef_constructor(const std::string &cname="") const
std::string doc_string(const std::string &meth_name) const
octave_value_list execute(octave::tree_evaluator &tw, int nargout, const octave_value_list &idx)
void print(std::ostream &os, bool pr_as_read_syntax=false)
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
octave_map map_value() const
dim_vector dims() const
octave_value_list subsref(const std::string &type, const std::list< octave_value_list > &idx, int nargout)
octave_value reshape(const dim_vector &new_dims) const
octave::cdef_object & get_object_ref()
Definition ov-classdef.h:74
bool save_ascii(std::ostream &os)
bool is_instance_of(const std::string &cls_name) const
octave_value undef_subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
octave::cdef_object get_object() const
Definition ov-classdef.h:72
static octave_value metaclass_query(const std::string &cls)
octave_idx_type xnumel(const octave_value_list &)
static octave_value superclass_ref(const std::string &meth, const std::string &cls)
std::string type_name() const
std::string class_name() const
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
bool load_ascii(std::istream &is)
string_vector map_keys() const
void loadobj(std::vector< std::tuple< octave_map, uint32_t, bool > > &m, dim_vector &dv)
std::vector< std::tuple< octave_map, uint32_t, bool > > saveobj(std::vector< bool > &is_new)
virtual bool is_classdef_constructor(const std::string &="") const
Definition ov-fcn.h:131
Cell getfield(const std::string &key) const
Definition oct-map.cc:275
octave_idx_type nfields() const
Definition oct-map.h:323
string_vector fieldnames() const
Definition oct-map.h:332
const Cell & contents(const_iterator p) const
Definition oct-map.h:310
bool isfield(const std::string &name) const
Definition oct-map.h:326
octave_idx_type numel() const
Definition oct-map.h:368
octave::tree_parameter_list * return_list()
Definition ov-usr-fcn.h:396
bool empty() const
Definition ovl.h:113
octave_value_list slice(octave_idx_type offset, octave_idx_type len, bool tags=false) const
Definition ovl.h:129
octave_idx_type length() const
Definition ovl.h:111
bool is_classdef_object() const
Definition ov.h:653
std::string class_name() const
Definition ov.h:1360
bool bool_value(bool warn=false) const
Definition ov.h:889
octave_classdef * classdef_object_value(bool silent=false) const
bool is_string() const
Definition ov.h:635
void short_disp(std::ostream &os) const
Definition ov.h:1346
Matrix size()
Definition ov.h:460
bool is_defined() const
Definition ov.h:590
octave_map map_value() const
std::string string_value(bool force=false) const
Definition ov.h:981
std::string type_name() const
Definition ov.h:1358
bool isobject() const
Definition ov.h:662
bool empty() const
Definition str-vec.h:75
octave_idx_type numel() const
Definition str-vec.h:98
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:1083
void error(const char *fmt,...)
Definition error.cc:1008
void err_wrong_type_arg(const char *name, const char *s)
Definition errwarn.cc:166
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)
octave_value make_idx_args(const std::string &type, const std::list< octave_value_list > &idx, const std::string &who)
Definition ov-base.cc:1450
bool called_from_builtin()
Definition ov-base.cc:1513
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
#define octave_stdout
Definition pager.h:301
bool Vcompact_format
Definition pr-output.cc:102
F77_RET_T len
Definition xerbla.cc:61