changelog shortlog tags changeset files revisions annotate raw

src/ov-struct.cc

changeset 10289: 4b124317dc38
parent:0efd486813fe
author: John W. Eaton <jwe@octave.org>
date: Tue Feb 09 20:58:55 2010 -0500 (59 minutes ago)
permissions: -rw-r--r--
description: base_properties::set_children: account for hidden children
1/*
2
3Copyright (C) 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005, 2006,
4 2007, 2008, 2009 John W. Eaton
5
6This file is part of Octave.
7
8Octave is free software; you can redistribute it and/or modify it
9under the terms of the GNU General Public License as published by the
10Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13Octave is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License
19along with Octave; see the file COPYING. If not, see
20<http://www.gnu.org/licenses/>.
21
22*/
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <iostream>
29
30#include "Cell.h"
31#include "defun.h"
32#include "error.h"
33#include "gripes.h"
34#include "oct-lvalue.h"
35#include "ov-struct.h"
36#include "unwind-prot.h"
37#include "utils.h"
38#include "variables.h"
39
40#include "Array-util.h"
41#include "oct-locbuf.h"
42
43#include "byte-swap.h"
44#include "ls-oct-ascii.h"
45#include "ls-oct-binary.h"
46#include "ls-hdf5.h"
47#include "ls-utils.h"
48#include "pr-output.h"
49
50DEFINE_OCTAVE_ALLOCATOR(octave_struct);
51
52DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA(octave_struct, "struct", "struct");
53
54Cell
55octave_struct::dotref (const octave_value_list& idx, bool auto_add)
56{
57 Cell retval;
58
59 assert (idx.length () == 1);
60
61 std::string nm = idx(0).string_value ();
62
63 Octave_map::const_iterator p = map.seek (nm);
64
65 if (p != map.end ())
66 retval = map.contents (p);
67 else if (auto_add)
68 retval = (numel () == 0) ? Cell (dim_vector (1)) : Cell (dims ());
69 else
70 error ("structure has no member `%s'", nm.c_str ());
71
72 return retval;
73}
74
75#if 0
76static void
77gripe_invalid_index (void)
78{
79 error ("invalid index for structure array");
80}
81#endif
82
83static void
84gripe_invalid_index_for_assignment (void)
85{
86 error ("invalid index for structure array assignment");
87}
88
89static void
90gripe_invalid_index_type (const std::string& nm, char t)
91{
92 error ("%s cannot be indexed with %c", nm.c_str (), t);
93}
94
95static void
96gripe_failed_assignment (void)
97{
98 error ("assignment to structure element failed");
99}
100
101octave_value_list
102octave_struct::subsref (const std::string& type,
103 const std::list<octave_value_list>& idx,
104 int nargout)
105{
106 octave_value_list retval;
107
108 int skip = 1;
109
110 switch (type[0])
111 {
112 case '(':
113 {
114 if (type.length () > 1 && type[1] == '.')
115 {
116 std::list<octave_value_list>::const_iterator p = idx.begin ();
117 octave_value_list key_idx = *++p;
118
119 const Cell tmp = dotref (key_idx);
120
121 if (! error_state)
122 {
123 const Cell t = tmp.index (idx.front ());
124
125 retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true);
126
127 // We handled two index elements, so tell
128 // next_subsref to skip both of them.
129
130 skip++;
131 }
132 }
133 else
134 retval(0) = map.index (idx.front ());
135 }
136 break;
137
138 case '.':
139 {
140 if (map.numel() > 0)
141 {
142 const Cell t = dotref (idx.front ());
143
144 retval(0) = (t.length () == 1) ? t(0) : octave_value (t, true);
145 }
146 }
147 break;
148
149 case '{':
150 gripe_invalid_index_type (type_name (), type[0]);
151 break;
152
153 default:
154 panic_impossible ();
155 }
156
157 // FIXME -- perhaps there should be an
158 // octave_value_list::next_subsref member function? See also
159 // octave_user_function::subsref.
160
161 if (idx.size () > 1)
162 retval = retval(0).next_subsref (nargout, type, idx, skip);
163
164 return retval;
165}
166
167octave_value
168octave_struct::subsref (const std::string& type,
169 const std::list<octave_value_list>& idx,
170 bool auto_add)
171{
172 octave_value retval;
173
174 int skip = 1;
175
176 switch (type[0])
177 {
178 case '(':
179 {
180 if (type.length () > 1 && type[1] == '.')
181 {
182 std::list<octave_value_list>::const_iterator p = idx.begin ();
183 octave_value_list key_idx = *++p;
184
185 const Cell tmp = dotref (key_idx, auto_add);
186
187 if (! error_state)
188 {
189 const Cell t = tmp.index (idx.front (), auto_add);
190
191 retval = (t.length () == 1) ? t(0) : octave_value (t, true);
192
193 // We handled two index elements, so tell
194 // next_subsref to skip both of them.
195
196 skip++;
197 }
198 }
199 else
200 retval = map.index (idx.front (), auto_add);
201 }
202 break;
203
204 case '.':
205 {
206 if (map.numel() > 0)
207 {
208 const Cell t = dotref (idx.front (), auto_add);
209
210 retval = (t.length () == 1) ? t(0) : octave_value (t, true);
211 }
212 }
213 break;
214
215 case '{':
216 gripe_invalid_index_type (type_name (), type[0]);
217 break;
218
219 default:
220 panic_impossible ();
221 }
222
223 // FIXME -- perhaps there should be an
224 // octave_value_list::next_subsref member function? See also
225 // octave_user_function::subsref.
226
227 if (idx.size () > 1)
228 retval = retval.next_subsref (auto_add, type, idx, skip);
229
230 return retval;
231}
232
233/*
234%!test
235%! x(1).a.a = 1; x(2).a.a = 2;
236%! assert (size (x), [1, 2]);
237%! assert (x(1).a.a, 1);
238%! assert (x(2).a.a, 2);
239*/
240
241octave_value
242octave_struct::numeric_conv (const octave_value& val,
243 const std::string& type)
244{
245 octave_value retval;
246
247 if (type.length () > 0 && type[0] == '.' && ! val.is_map ())
248 retval = Octave_map ();
249 else
250 retval = val;
251
252 return retval;
253}
254
255octave_value
256octave_struct::subsasgn (const std::string& type,
257 const std::list<octave_value_list>& idx,
258 const octave_value& rhs)
259{
260 octave_value retval;
261
262 int n = type.length ();
263
264 octave_value t_rhs = rhs;
265
266 if (idx.front ().empty ())
267 {
268 error ("missing index in indexed assignment");
269 return retval;
270 }
271
272 if (n > 1 && ! (type.length () == 2 && type[0] == '(' && type[1] == '.'))
273 {
274 switch (type[0])
275 {
276 case '(':
277 {
278 if (type.length () > 1 && type[1] == '.')
279 {
280 std::list<octave_value_list>::const_iterator p = idx.begin ();
281 octave_value_list t_idx = *p;
282
283 octave_value_list key_idx = *++p;
284
285 assert (key_idx.length () == 1);
286
287 std::string key = key_idx(0).string_value ();
288
289 std::list<octave_value_list> next_idx (idx);
290
291 // We handled two index elements, so subsasgn to
292 // needs to skip both of them.
293
294 next_idx.erase (next_idx.begin ());
295 next_idx.erase (next_idx.begin ());
296
297 std::string next_type = type.substr (2);
298
299 Cell tmpc (1, 1);
300 Octave_map::iterator pkey = map.seek (key);
301 if (pkey != map.end ())
302 {
303 pkey->second.make_unique ();
304 tmpc = pkey->second.index (idx.front (), true);
305 }
306
307 // FIXME: better code reuse? cf. octave_cell::subsasgn and the case below.
308 if (! error_state)
309 {
310 if (tmpc.numel () == 1)
311 {
312 octave_value& tmp = tmpc(0);
313
314 if (! tmp.is_defined () || tmp.is_zero_by_zero ())
315 {
316 tmp = octave_value::empty_conv (next_type, rhs);
317 tmp.make_unique (); // probably a no-op.
318 }
319 else
320 // optimization: ignore the copy still stored inside our map.
321 tmp.make_unique (1);
322
323 if (! error_state)
324 t_rhs = tmp.subsasgn (next_type, next_idx, rhs);
325 }
326 else
327 gripe_indexed_cs_list ();
328 }
329 }
330 else
331 gripe_invalid_index_for_assignment ();
332 }
333 break;
334
335 case '.':
336 {
337 octave_value_list key_idx = idx.front ();
338
339 assert (key_idx.length () == 1);
340
341 std::string key = key_idx(0).string_value ();
342
343 std::list<octave_value_list> next_idx (idx);
344
345 next_idx.erase (next_idx.begin ());
346
347 std::string next_type = type.substr (1);
348
349 Cell tmpc (1, 1);
350 Octave_map::iterator pkey = map.seek (key);
351 if (pkey != map.end ())
352 {
353 pkey->second.make_unique ();
354 tmpc = pkey->second;
355 }
356
357 // FIXME: better code reuse?
358 if (! error_state)
359 {
360 if (tmpc.numel () == 1)
361 {
362 octave_value& tmp = tmpc(0);
363
364 if (! tmp.is_defined () || tmp.is_zero_by_zero ())
365 {
366 tmp = octave_value::empty_conv (next_type, rhs);
367 tmp.make_unique (); // probably a no-op.
368 }
369 else
370 // optimization: ignore the copy still stored inside our map.
371 tmp.make_unique (1);
372
373 if (! error_state)
374 t_rhs = tmp.subsasgn (next_type, next_idx, rhs);
375 }
376 else
377 gripe_indexed_cs_list ();
378 }
379 }
380 break;
381
382 case '{':
383 gripe_invalid_index_type (type_name (), type[0]);
384 break;
385
386 default:
387 panic_impossible ();
388 }
389 }
390
391 if (! error_state)
392 {
393 switch (type[0])
394 {
395 case '(':
396 {
397 if (n > 1 && type[1] == '.')
398 {
399 std::list<octave_value_list>::const_iterator p = idx.begin ();
400 octave_value_list key_idx = *++p;
401 octave_value_list idxf = idx.front ();
402
403 assert (key_idx.length () == 1);
404
405 std::string key = key_idx(0).string_value ();
406
407 if (! error_state)
408 {
409 if (t_rhs.is_cs_list ())
410 {
411 Cell tmp_cell = Cell (t_rhs.list_value ());
412
413 // Inquire the proper shape of the RHS.
414
415 dim_vector didx = dims ().redim (idxf.length ());
416 for (octave_idx_type k = 0; k < idxf.length (); k++)
417 if (! idxf(k).is_magic_colon ()) didx(k) = idxf(k).numel ();
418
419 if (didx.numel () == tmp_cell.numel ())
420 tmp_cell = tmp_cell.reshape (didx);
421
422
423 map.assign (idxf, key, tmp_cell);
424
425 if (! error_state)
426 {
427 count++;
428 retval = octave_value (this);
429 }
430 else
431 gripe_failed_assignment ();
432 }
433 else
434 {
435 const Octave_map& cmap = const_cast<const Octave_map &> (map);
436 // cast map to const reference to avoid forced key insertion.
437 if (idxf.all_scalars ()
438 || cmap.contents (key).index (idxf, true).numel () == 1)
439 {
440 map.assign (idxf, key, t_rhs.storable_value ());
441 if (! error_state)
442 {
443 count++;
444 retval = octave_value (this);
445 }
446 else
447 gripe_failed_assignment ();
448 }
449 else if (! error_state)
450 gripe_nonbraced_cs_list_assignment ();
451 }
452 }
453 else
454 gripe_failed_assignment ();
455 }
456 else
457 {
458 if (t_rhs.is_map())
459 {
460 Octave_map rhs_map = t_rhs.map_value ();
461
462 if (! error_state)
463 {
464 map.assign (idx.front (), rhs_map);
465
466 if (! error_state)
467 {
468 count++;
469 retval = octave_value (this);
470 }
471 else
472 gripe_failed_assignment ();
473 }
474 else
475 error ("invalid structure assignment");
476 }
477 else
478 {
479 if (t_rhs.is_null_value())
480 {
481 map.maybe_delete_elements (idx.front());
482
483 if (! error_state)
484 {
485 count++;
486 retval = octave_value (this);
487 }
488 else
489 gripe_failed_assignment ();
490 }
491 else
492 error ("invalid structure assignment");
493 }
494 }
495 }
496 break;
497
498 case '.':
499 {
500 octave_value_list key_idx = idx.front ();
501
502 assert (key_idx.length () == 1);
503
504 std::string key = key_idx(0).string_value ();
505
506 if (t_rhs.is_cs_list ())
507 {
508 Cell tmp_cell = Cell (t_rhs.list_value ());
509
510 // The shape of the RHS is irrelevant, we just want
511 // the number of elements to agree and to preserve the
512 // shape of the left hand side of the assignment.
513
514 if (numel () == tmp_cell.numel ())
515 tmp_cell = tmp_cell.reshape (dims ());
516
517 map.assign (key, tmp_cell);
518 }
519 else
520 // Regularize a null matrix if stored into a struct component.
521 map.assign (key, t_rhs.storable_value ());
522
523 if (! error_state)
524 {
525 count++;
526 retval = octave_value (this);
527 }
528 else
529 gripe_failed_assignment ();
530 }
531 break;
532
533 case '{':
534 gripe_invalid_index_type (type_name (), type[0]);
535 break;
536
537 default:
538 panic_impossible ();
539 }
540 }
541 else
542 gripe_failed_assignment ();
543
544 return retval;
545}
546
547octave_value
548octave_struct::do_index_op (const octave_value_list& idx, bool resize_ok)
549{
550 // Octave_map handles indexing itself.
551 return map.index (idx, resize_ok);
552}
553
554size_t
555octave_struct::byte_size (void) const
556{
557 // Neglect the size of the fieldnames.
558
559 size_t retval = 0;
560
561 for (Octave_map::const_iterator p = map.begin (); p != map.end (); p++)
562 {
563 std::string key = map.key (p);
564
565 octave_value val = octave_value (map.contents (p));
566
567 retval += val.byte_size ();
568 }
569
570 return retval;
571}
572
573void
574octave_struct::print (std::ostream& os, bool) const
575{
576 print_raw (os);
577}
578
579void
580octave_struct::print_raw (std::ostream& os, bool) const
581{
582 unwind_protect frame;
583
584 frame.protect_var (Vstruct_levels_to_print);
585
586 if (Vstruct_levels_to_print >= 0)
587 {
588 bool print_keys_only = Vstruct_levels_to_print-- == 0;
589
590 indent (os);
591 os << "{";
592 newline (os);
593
594 increment_indent_level ();
595
596 octave_idx_type n = map.numel ();
597
598 if (n != 1 || print_keys_only)
599 {
600 indent (os);
601 dim_vector dv = dims ();
602 os << dv.str () << " struct array containing the fields:";
603 newline (os);
604 newline (os);
605
606 increment_indent_level ();
607 }
608
609 string_vector key_list = map.keys ();
610
611 for (octave_idx_type i = 0; i < key_list.length (); i++)
612 {
613 std::string key = key_list[i];
614
615 Cell val = map.contents (key);
616
617 octave_value tmp = (n == 1) ? val(0) : octave_value (val, true);
618
619 if (n != 1 || print_keys_only)
620 {
621 indent (os);
622 os << key;
623 if (n == 1)
624 {
625 dim_vector dv = tmp.dims ();
626 os << ": " << dv.str () << " " << tmp.type_name ();
627 }
628 newline (os);
629 }
630 else
631 tmp.print_with_name (os, key);
632 }
633
634 if (n != 1 || print_keys_only)
635 decrement_indent_level ();
636
637 decrement_indent_level ();
638
639 indent (os);
640 os << "}";
641 newline (os);
642 }
643 else
644 {
645 indent (os);
646 os << "<structure>";
647 newline (os);
648 }
649}
650
651bool
652octave_struct::print_name_tag (std::ostream& os, const std::string& name) const
653{
654 bool retval = false;
655
656 indent (os);
657
658 if (Vstruct_levels_to_print < 0)
659 os << name << " = ";
660 else
661 {
662 os << name << " =";
663 newline (os);
664 retval = true;
665 }
666
667 return retval;
668}
669
670static bool
671scalar (const dim_vector& dims)
672{
673 return dims.length () == 2 && dims (0) == 1 && dims (1) == 1;
674}
675
676/*
677%!shared x
678%! x(1).a=1; x(2).a=2; x(1).b=3; x(2).b=3;
679%!assert(struct('a',1,'b',3),x(1))
680%!assert(isempty(x([])))
681%!assert(isempty(struct('a',{},'b',{})))
682%!assert(struct('a',{1,2},'b',{3,3}),x)
683%!assert(struct('a',{1,2},'b',3),x)
684%!assert(struct('a',{1,2},'b',{3}),x)
685%!assert(struct('b',3,'a',{1,2}),x)
686%!assert(struct('b',{3},'a',{1,2}),x)
687%!test x=struct([]);
688%!assert(size(x),[0,0]);
689%!assert(isstruct(x));
690%!assert(isempty(fieldnames(x)));
691%!fail("struct('a',{1,2},'b',{1,2,3})","dimensions of parameter 2 do not match those of parameter 4")
692%!fail("struct(1,2,3,4)","struct expects alternating \"field\", VALUE pairs");
693%!fail("struct('1',2,'3')","struct expects alternating \"field\", VALUE pairs");
694*/
695
696DEFUN (struct, args, ,
697 "-*- texinfo -*-\n\
698@deftypefn {Built-in Function} {} struct (\"field\", @var{value}, \"field\", @var{value}, @dots{})\n\
699\n\
700Create a structure and initialize its value.\n\
701\n\
702If the values are cell arrays, create a structure array and initialize\n\
703its values. The dimensions of each cell array of values must match.\n\
704Singleton cells and non-cell values are repeated so that they fill\n\
705the entire array. If the cells are empty, create an empty structure\n\
706array with the specified field names.\n\
707\n\
708If the argument is an object, return the underlying struct.\n\
709@end deftypefn")
710{
711 octave_value retval;
712
713 int nargin = args.length ();
714
715 // struct ([]) returns an empty struct.
716
717 // struct (empty_matrix) returns an empty struct with the same
718 // dimensions as the empty matrix.
719
720 // Note that struct () creates a 1x1 struct with no fields for
721 // compatibility with Matlab.
722
723 if (nargin == 1 && args(0).is_object ())
724 {
725 Octave_map m = args(0).map_value ();
726 retval = octave_value (new octave_struct (m));
727
728 return retval;
729 }
730
731 if ((nargin == 1 || nargin == 2)
732 && args(0).is_empty () && args(0).is_real_matrix ())
733 {
734 Cell fields;
735
736 if (nargin == 2)
737 {
738 if (args(1).is_cellstr ())
739 retval = Octave_map (args(0).dims (), args(1).cell_value ());
740 else
741 error ("struct: expecting cell array of field names as second argument");
742 }
743 else
744 retval = Octave_map (args(0).dims ());
745
746 return retval;
747 }
748
749 // Check for "field", VALUE pairs.
750
751 for (int i = 0; i < nargin; i += 2)
752 {
753 if (! args(i).is_string () || i + 1 >= nargin)
754 {
755 error ("struct expects alternating \"field\", VALUE pairs");
756 return retval;
757 }
758 }
759
760 // Check that the dimensions of the values correspond.
761
762 dim_vector dims (1, 1);
763
764 int first_dimensioned_value = 0;
765
766 for (int i = 1; i < nargin; i += 2)
767 {
768 if (args(i).is_cell ())
769 {
770 dim_vector argdims (args(i).dims ());
771
772 if (! scalar (argdims))
773 {
774 if (! first_dimensioned_value)
775 {
776 dims = argdims;
777 first_dimensioned_value = i + 1;
778 }
779 else if (dims != argdims)
780 {
781 error ("struct: dimensions of parameter %d do not match those of parameter %d",
782 first_dimensioned_value, i+1);
783 return retval;
784 }
785 }
786 }
787 }
788
789 // Create the return value.
790
791 Octave_map map (dims);
792
793 for (int i = 0; i < nargin; i+= 2)
794 {
795 // Get key.
796
797 std::string key (args(i).string_value ());
798
799 if (error_state)
800 return retval;
801
802 if (! valid_identifier (key))
803 {
804 error ("struct: invalid structure field name `%s'", key.c_str ());
805 return retval;
806 }
807
808 // Value may be v, { v }, or { v1, v2, ... }
809 // In the first two cases, we need to create a cell array of
810 // the appropriate dimensions filled with v. In the last case,
811 // the cell array has already been determined to be of the
812 // correct dimensions.
813
814 if (args(i+1).is_cell ())
815 {
816 const Cell c (args(i+1).cell_value ());
817
818 if (error_state)
819 return retval;
820
821 if (scalar (c.dims ()))
822 map.assign (key, Cell (dims, c(0)));
823 else
824 map.assign (key, c);
825 }
826 else
827 map.assign (key, Cell (dims, args(i+1)));
828
829 if (error_state)
830 return retval;
831 }
832
833 return octave_value (map);
834}
835
836DEFUN (isstruct, args, ,
837 "-*- texinfo -*-\n\
838@deftypefn {Built-in Function} {} isstruct (@var{expr})\n\
839Return 1 if the value of the expression @var{expr} is a structure\n\
840(or a structure array).\n\
841@end deftypefn")
842{
843 octave_value retval;
844
845 if (args.length () == 1)
846 retval = args(0).is_map ();
847 else
848 print_usage ();
849
850 return retval;
851}
852
853DEFUN (fieldnames, args, ,
854 "-*- texinfo -*-\n\
855@deftypefn {Built-in Function} {} fieldnames (@var{struct})\n\
856Return a cell array of strings naming the elements of the structure\n\
857@var{struct}. It is an error to call @code{fieldnames} with an\n\
858argument that is not a structure.\n\
859@end deftypefn")
860{
861 octave_value retval;
862
863 int nargin = args.length ();
864
865 if (nargin == 1)
866 {
867 octave_value arg = args(0);
868
869 if (arg.is_map () || arg.is_object ())
870 {
871 Octave_map m = arg.map_value ();
872
873 string_vector keys = m.keys ();
874
875 if (keys.length () == 0)
876 retval = Cell (0, 1);
877 else
878 retval = Cell (m.keys ());
879 }
880 else
881 gripe_wrong_type_arg ("fieldnames", args(0));
882 }
883 else
884 print_usage ();
885
886 return retval;
887}
888
889/*
890%!# test preservation of fieldname order
891%!test
892%! x(3).d=1; x(2).a=2; x(1).b=3; x(2).c=3;
893%! assert(fieldnames(x), {"d"; "a"; "b"; "c"});
894*/
895
896DEFUN (isfield, args, ,
897 "-*- texinfo -*-\n\
898@deftypefn {Built-in Function} {} isfield (@var{expr}, @var{name})\n\
899Return true if the expression @var{expr} is a structure and it includes an\n\
900element named @var{name}. The first argument must be a structure and\n\
901the second must be a string.\n\
902@end deftypefn")
903{
904 octave_value retval;
905
906 int nargin = args.length ();
907
908 if (nargin == 2)
909 {
910 retval = false;
911
912 // FIXME -- should this work for all types that can do
913 // structure reference operations?
914
915 if (args(0).is_map () && args(1).is_string ())
916 {
917 std::string key = args(1).string_value ();
918
919 Octave_map m = args(0).map_value ();
920
921 retval = m.contains (key) != 0;
922 }
923 }
924 else
925 print_usage ();
926
927 return retval;
928}
929
930DEFUN (nfields, args, ,
931 "-*- texinfo -*-\n\
932@deftypefn {Built-in Function} {} nfields (@var{s})\n\
933Return the number of fields of the structure @var{s}.\n\
934@end deftypefn")
935{
936 octave_value retval;
937
938 int nargin = args.length ();
939
940 if (nargin == 1 && args(0).is_map ())
941 {
942 retval = static_cast<double> (args(0).nfields ());
943 }
944 else
945 print_usage ();
946
947 return retval;
948}
949
950/*
951%!# test isfield
952%!test
953%! x(3).d=1; x(2).a=2; x(1).b=3; x(2).c=3;
954%! assert (isfield (x, "b"));
955%!assert( isfield( struct("a", "1"), "a"));
956%!assert( isfield( {1}, "c"), logical (0));
957%!assert( isfield( struct("a", "1"), 10), logical (0));
958%!assert( isfield( struct("a", "b"), "a "), logical (0));
959
960*/
961
962// Check that the dimensions of the input arguments are correct.
963
964static bool
965cell2struct_check_args (const dim_vector& c_dv, const dim_vector& f_dv,
966 bool is_cell, int dim)
967{
968 bool retval = true;
969
970 if (dim >= 0 && dim < c_dv.length ())
971 {
972 if (is_cell)
973 {
974 if (f_dv.numel () != c_dv(dim))
975 {
976 error ("cell2struct: numel (FIELD) != size (CELL, DIM)");
977
978 retval = false;
979 }
980 }
981 else
982 {
983 if (f_dv.length () > 2)
984 {
985 error ("cell2struct: field array must be a 2-d matrix");
986
987 retval = false;
988 }
989 else if (f_dv(0) != c_dv(dim))
990 {
991 error ("cell2struct: size (FIELD, 1) != length (C, DIM)");
992
993 retval = false;
994 }
995 }
996 }
997 else
998 {
999 error ("cell2struct: DIM out of range");
1000
1001 retval = false;
1002 }
1003
1004 return retval;
1005}
1006
1007static void
1008cell2struct_construct_idx (Array<octave_idx_type>& ra_idx1,
1009 const Array<octave_idx_type>& ra_idx2,
1010 octave_idx_type dim, octave_idx_type fill_value)
1011{
1012 octave_idx_type iidx = 0;
1013
1014 for (octave_idx_type idx = 0; idx < ra_idx1.length (); idx++)
1015 {
1016 if (idx == dim)
1017 ra_idx1.elem (idx) = fill_value;
1018 else
1019 ra_idx1.elem (idx) = ra_idx2(iidx++);
1020 }
1021}
1022
1023DEFUN (cell2struct, args, ,
1024 "-*- texinfo -*-\n\
1025@deftypefn {Built-in Function} {} cell2struct (@var{cell}, @var{fields}, @var{dim})\n\
1026Convert @var{cell} to a structure. The number of fields in @var{fields}\n\
1027must match the number of elements in @var{cell} along dimension @var{dim},\n\
1028that is @code{numel (@var{fields}) == size (@var{cell}, @var{dim})}.\n\
1029\n\
1030@example\n\
1031@group\n\
1032A = cell2struct (@{'Peter', 'Hannah', 'Robert';\n\
1033 185, 170, 168@},\n\
1034 @{'Name','Height'@}, 1);\n\
1035A(1)\n\
1036 @result{} ans =\n\
1037 @{\n\
1038 Name = Peter\n\
1039 Height = 185\n\
1040 @}\n\
1041\n\
1042@end group\n\
1043@end example\n\
1044@end deftypefn")
1045{
1046 octave_value retval;
1047
1048 if (args.length () == 3)
1049 {
1050 Cell c = args(0).cell_value ();
1051
1052 if (! error_state)
1053 {
1054 octave_value field = args(1);
1055
1056 // Field is either cell or character matrix.
1057
1058 // FIXME -- this could be simplified if we had
1059 // cellstr and iscellstr functions available.
1060
1061 bool field_is_cell = field.is_cell ();
1062
1063 Cell field_cell;
1064 charMatrix field_char;
1065
1066 if (field_is_cell)
1067 field_cell = field.cell_value ();
1068 else
1069 field_char = field.char_matrix_value ();
1070
1071 if (! error_state)
1072 {
1073 // Retrieve the dimension value.
1074
1075 // FIXME -- int_value () should print out the
1076 // conversions it does to be Matlab compatible.
1077
1078 octave_idx_type dim = args(2).int_value () - 1;
1079
1080 if (! error_state)
1081 {
1082 dim_vector c_dv = c.dims ();
1083 dim_vector field_dv = field.dims ();
1084
1085 if (cell2struct_check_args (c_dv, field_dv, field_is_cell,
1086 dim))
1087 {
1088 octave_idx_type c_dv_length = c_dv.length ();
1089
1090 // Dimension vector for the Cell arrays to be
1091 // put into the structure.
1092
1093 dim_vector value_dv;
1094
1095 // Initialize c_value_dv.
1096
1097 if (c_dv_length == 2)
1098 value_dv = dim_vector (1, 1);
1099 else
1100 value_dv.resize (c_dv_length - 1);
1101
1102 octave_idx_type idx_tmp = 0;
1103
1104 for (octave_idx_type i = 0; i < c_dv_length; i++)
1105 {
1106 if (i != dim)
1107 value_dv.elem (idx_tmp++) = c_dv.elem (i);
1108 }
1109
1110 // All initializing is done, we can start moving
1111 // values.
1112
1113 Octave_map map;
1114
1115 // If field is a cell array then we use all
1116 // elements in array, on the other hand when
1117 // field is a character array the number of
1118 // elements is equals the number of rows.
1119
1120 octave_idx_type field_numel
1121 = field_is_cell ? field_dv.numel (): field_dv(0);
1122
1123 // For matlab compatibility.
1124
1125 if (field_numel == 0)
1126 map.reshape (dim_vector (0, 1));
1127
1128 for (octave_idx_type i = 0; i < field_numel; i++)
1129 {
1130 // Construct cell array which goes into the
1131 // structure together with the appropriate
1132 // field name.
1133
1134 Cell c_value (value_dv);
1135
1136 Array<octave_idx_type> value_idx (value_dv.length (), 0);
1137 Array<octave_idx_type> c_idx (c_dv_length, 0);
1138
1139 for (octave_idx_type j = 0; j < value_dv.numel (); j++)
1140 {
1141 // Need to do this to construct the
1142 // appropriate idx for getting elements
1143 // from the original cell array.
1144
1145 cell2struct_construct_idx (c_idx, value_idx,
1146 dim, i);
1147
1148 c_value.elem (value_idx) = c.elem (c_idx);
1149
1150 increment_index (value_idx, value_dv);
1151 }
1152
1153 std::string field_str;
1154
1155 if (field_is_cell)
1156 {
1157 // Matlab retrieves the field values
1158 // column by column.
1159
1160 octave_value field_tmp = field_cell.elem (i);
1161
1162 field_str = field_tmp.string_value ();
1163
1164 if (error_state)
1165 {
1166 error ("cell2struct: fields have to be of type string");
1167 break;
1168 }
1169 }
1170 else
1171 {
1172 field_str = field_char.row_as_string (i);
1173
1174 if (error_state)
1175 return retval;
1176 }
1177
1178 if (! valid_identifier (field_str))
1179 {
1180 error ("cell2struct: invalid field name `%s'",
1181 field_str.c_str ());
1182 break;
1183 }
1184
1185 map.reshape (value_dv);
1186
1187 map.assign (field_str, c_value);
1188 }
1189
1190 if (! error_state)
1191 retval = map;
1192 }
1193 }
1194 else
1195 error ("cell2struct: expecting third argument to be an integer");
1196 }
1197 else
1198 error ("cell2struct: expecting second argument to be a cell or character array");
1199 }
1200 else
1201 error ("cell2struct: expecting first argument to be a cell array");
1202 }
1203 else
1204 print_usage ();
1205
1206 return retval;
1207}
1208
1209/*
1210%!# test cell2struct versus struct2cell
1211%!test
1212%! keys = cellstr (char (floor (rand (100,10)*24+65)))';
1213%! vals = mat2cell(rand (100,1), ones (100,1), 1)';
1214%! s = struct ([keys; vals]{:});
1215%! t = cell2struct (vals, keys, 2);
1216%! assert (s, t);
1217%! assert (struct2cell (s), vals');
1218%! assert (fieldnames (s), keys');
1219*/
1220
1221
1222// So we can call Fcellstr directly.
1223extern octave_value_list Fcellstr (const octave_value_list& args, int);
1224
1225DEFUN (rmfield, args, ,
1226 "-*- texinfo -*-\n\
1227@deftypefn {Built-in Function} {} rmfield (@var{s}, @var{f})\n\
1228Return a copy of the structure (array) @var{s} with the field @var{f} removed.\n\
1229If @var{f} is a cell array of strings or a character array, remove the\n\
1230named fields.\n\
1231@seealso{cellstr, iscellstr, setfield}\n\
1232@end deftypefn")
1233{
1234 octave_value retval;
1235
1236 int nargin = args.length ();
1237
1238 if (nargin == 2)
1239 {
1240 Octave_map m = args(0).map_value ();
1241
1242 octave_value_list fval = Fcellstr (args(1), 1);
1243
1244 if (! error_state)
1245 {
1246 Cell fcell = fval(0).cell_value ();
1247
1248 for (int i = 0; i < fcell.numel (); i++)
1249 {
1250 std::string key = fcell(i).string_value ();
1251
1252 if (m.contains (key))
1253 m.del (key);
1254 else
1255 {
1256 error ("rmfield: structure does not contain field %s",
1257 key.c_str ());
1258
1259 break;
1260 }
1261 }
1262
1263 if (! error_state)
1264 retval = m;
1265 }
1266 }
1267 else
1268 print_usage ();
1269
1270 return retval;
1271}
1272
1273/*
1274%!# test rmfield
1275%!test
1276%! x(3).d=1; x(2).a=2; x(1).b=3; x(2).c=3; x(6).f="abc123";
1277%! y = rmfield (x, {"a", "f"});
1278%! assert (fieldnames (y), {"d"; "b"; "c"});
1279%! assert (size (y), [1, 6]);
1280*/
1281
1282bool
1283octave_struct::save_ascii (std::ostream& os)
1284{
1285 Octave_map m = map_value ();
1286
1287 octave_idx_type nf = m.nfields ();
1288
1289 const dim_vector dv = dims ();
1290
1291 os << "# ndims: " << dv.length () << "\n";
1292
1293 for (int i = 0; i < dv.length (); i++)
1294 os << " " << dv (i);
1295 os << "\n";
1296
1297 os << "# length: " << nf << "\n";
1298
1299 // Iterating over the list of keys will preserve the order of the
1300 // fields.
1301 string_vector keys = m.keys ();
1302
1303 for (octave_idx_type i = 0; i < nf; i++)
1304 {
1305 std::string key = keys(i);
1306
1307 octave_value val = map.contents (key);
1308
1309 bool b = save_ascii_data (os, val, key, false, 0);
1310
1311 if (! b)
1312 return os;
1313 }
1314
1315 return true;
1316}
1317
1318bool
1319octave_struct::load_ascii (std::istream& is)
1320{
1321 octave_idx_type len = 0;
1322 dim_vector dv (1, 1);
1323 bool success = true;
1324
1325 // KLUGE: earlier Octave versions did not save extra dimensions with struct,
1326 // and as a result did not preserve dimensions for empty structs.
1327 // The default dimensions were 1x1, which we want to preserve.
1328 string_vector keywords(2);
1329
1330 keywords[0] = "ndims";
1331 keywords[1] = "length";
1332
1333 std::string kw;
1334
1335 if (extract_keyword (is, keywords, kw, len, true))
1336 {
1337 if (kw == keywords[0])
1338 {
1339 int mdims = std::max (static_cast<int> (len), 2);
1340 dv.resize (mdims);
1341 for (int i = 0; i < mdims; i++)
1342 is >> dv(i);
1343
1344 success = extract_keyword (is, keywords[1], len);
1345 }
1346 }
1347 else
1348 success = false;
1349
1350 if (success && len >= 0)
1351 {
1352 if (len > 0)
1353 {
1354 Octave_map m (dv);
1355
1356 for (octave_idx_type j = 0; j < len; j++)
1357 {
1358 octave_value t2;
1359 bool dummy;
1360
1361 // recurse to read cell elements
1362 std::string nm
1363 = read_ascii_data (is, std::string (), dummy, t2, j);
1364
1365 if (!is)
1366 break;
1367
1368 Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
1369
1370 if (error_state)
1371 {
1372 error ("load: internal error loading struct elements");
1373 return false;
1374 }
1375
1376 m.assign (nm, tcell);
1377 }
1378
1379 if (is)
1380 map = m;
1381 else
1382 {
1383 error ("load: failed to load structure");
1384 success = false;
1385 }
1386 }
1387 else if (len == 0 )
1388 map = Octave_map (dv);
1389 else
1390 panic_impossible ();
1391 }
1392 else {
1393 error ("load: failed to extract number of elements in structure");
1394 success = false;
1395 }
1396
1397 return success;
1398}
1399
1400bool
1401octave_struct::save_binary (std::ostream& os, bool& save_as_floats)
1402{
1403 Octave_map m = map_value ();
1404
1405 octave_idx_type nf = m.nfields ();
1406
1407 dim_vector d = dims ();
1408 if (d.length () < 1)
1409 return false;
1410
1411 // Use negative value for ndims
1412 int32_t di = - d.length();
1413 os.write (reinterpret_cast<char *> (&di), 4);
1414 for (int i = 0; i < d.length (); i++)
1415 {
1416 di = d(i);
1417 os.write (reinterpret_cast<char *> (&di), 4);
1418 }
1419
1420 int32_t len = nf;
1421 os.write (reinterpret_cast<char *> (&len), 4);
1422
1423 // Iterating over the list of keys will preserve the order of the
1424 // fields.
1425 string_vector keys = m.keys ();
1426
1427 for (octave_idx_type i = 0; i < nf; i++)
1428 {
1429 std::string key = keys(i);
1430
1431 octave_value val = map.contents (key);
1432
1433 bool b = save_binary_data (os, val, key, "", 0, save_as_floats);
1434
1435 if (! b)
1436 return os;
1437 }
1438
1439 return true;
1440}
1441
1442bool
1443octave_struct::load_binary (std::istream& is, bool swap,
1444 oct_mach_info::float_format fmt)
1445{
1446 bool success = true;
1447 int32_t len;
1448 if (! is.read (reinterpret_cast<char *> (&len), 4))
1449 return false;
1450 if (swap)
1451 swap_bytes<4> (&len);
1452
1453 dim_vector dv (1, 1);
1454
1455 if (len < 0)
1456 {
1457 // We have explicit dimensions.
1458 int mdims = -len;
1459
1460 int32_t di;
1461 dv.resize (mdims);
1462
1463 for (int i = 0; i < mdims; i++)
1464 {
1465 if (! is.read (reinterpret_cast<char *> (&di), 4))
1466 return false;
1467 if (swap)
1468 swap_bytes<4> (&di);
1469 dv(i) = di;
1470 }
1471
1472 if (! is.read (reinterpret_cast<char *> (&len), 4))
1473 return false;
1474 if (swap)
1475 swap_bytes<4> (&len);
1476 }
1477
1478 if (len > 0)
1479 {
1480 Octave_map m (dv);
1481
1482 for (octave_idx_type j = 0; j < len; j++)
1483 {
1484 octave_value t2;
1485 bool dummy;
1486 std::string doc;
1487
1488 // recurse to read cell elements
1489 std::string nm = read_binary_data (is, swap, fmt, std::string (),
1490 dummy, t2, doc);
1491
1492 if (!is)
1493 break;
1494
1495 Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
1496
1497 if (error_state)
1498 {
1499 error ("load: internal error loading struct elements");
1500 return false;
1501 }
1502
1503 m.assign (nm, tcell);
1504 }
1505
1506 if (is)
1507 map = m;
1508 else
1509 {
1510 error ("load: failed to load structure");
1511 success = false;
1512 }
1513 }
1514 else if (len == 0)
1515 map = Octave_map (dv);
1516 else
1517 success = false;
1518
1519 return success;
1520}
1521
1522#if defined (HAVE_HDF5)
1523
1524bool
1525octave_struct::save_hdf5 (hid_t loc_id, const char *name, bool save_as_floats)
1526{
1527 hid_t data_hid = -1;
1528
1529#if HAVE_HDF5_18
1530 data_hid = H5Gcreate (loc_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
1531#else
1532 data_hid = H5Gcreate (loc_id, name, 0);
1533#endif
1534 if (data_hid < 0) return false;
1535
1536 // recursively add each element of the structure to this group
1537 Octave_map m = map_value ();
1538
1539 octave_idx_type nf = m.nfields ();
1540
1541 // Iterating over the list of keys will preserve the order of the
1542 // fields.
1543 string_vector keys = m.keys ();
1544
1545 for (octave_idx_type i = 0; i < nf; i++)
1546 {
1547 std::string key = keys(i);
1548
1549 octave_value val = map.contents (key);
1550
1551 bool retval2 = add_hdf5_data (data_hid, val, key, "", false,
1552 save_as_floats);
1553
1554 if (! retval2)
1555 break;
1556 }
1557
1558 H5Gclose (data_hid);
1559
1560 return true;
1561}
1562
1563bool
1564octave_struct::load_hdf5 (hid_t loc_id, const char *name)
1565{
1566 bool retval = false;
1567
1568 hdf5_callback_data dsub;
1569
1570 herr_t retval2 = 0;
1571 Octave_map m (dim_vector (1, 1));
1572 int current_item = 0;
1573 hsize_t num_obj = 0;
1574#if HAVE_HDF5_18
1575 hid_t group_id = H5Gopen (loc_id, name, H5P_DEFAULT);
1576#else
1577 hid_t group_id = H5Gopen (loc_id, name);
1578#endif
1579 H5Gget_num_objs (group_id, &num_obj);
1580 H5Gclose (group_id);
1581
1582 // FIXME -- fields appear to be sorted alphabetically on loading.
1583 // Why is that happening?
1584
1585 while (current_item < static_cast<int> (num_obj)
1586 && (retval2 = H5Giterate (loc_id, name, &current_item,
1587 hdf5_read_next_data, &dsub)) > 0)
1588 {
1589 octave_value t2 = dsub.tc;
1590
1591 Cell tcell = t2.is_cell () ? t2.cell_value () : Cell (t2);
1592
1593 if (error_state)
1594 {
1595 error ("load: internal error loading struct elements");
1596 return false;
1597 }
1598
1599 m.assign (dsub.name, tcell);
1600
1601 }
1602
1603 if (retval2 >= 0)
1604 {
1605 map = m;
1606 retval = true;
1607 }
1608
1609 return retval;
1610}
1611
1612#endif
1613
1614mxArray *
1615octave_struct::as_mxArray (void) const
1616{
1617 int nf = nfields ();
1618 string_vector kv = map_keys ();
1619
1620 OCTAVE_LOCAL_BUFFER (const char *, f, nf);
1621
1622 for (int i = 0; i < nf; i++)
1623 f[i] = kv[i].c_str ();
1624
1625 mxArray *retval = new mxArray (dims (), nf, f);
1626
1627 mxArray **elts = static_cast<mxArray **> (retval->get_data ());
1628
1629 mwSize nel = numel ();
1630
1631 mwSize ntot = nf * nel;
1632
1633 for (int i = 0; i < nf; i++)
1634 {
1635 Cell c = map.contents (kv[i]);
1636
1637 const octave_value *p = c.data ();
1638
1639 mwIndex k = 0;
1640 for (mwIndex j = i; j < ntot; j += nf)
1641 elts[j] = new mxArray (p[k++]);
1642 }
1643
1644 return retval;
1645}