1 // Copyright (C) 2009 VZLU Prague
2 // Copyright (C) 2018 John Donoghue <john.donogue@ieee.org>
3 //
4 // This program is free software; you can redistribute it and/or modify it under
5 // the terms of the GNU General Public License as published by the Free Software
6 // Foundation; either version 3 of the License, or (at your option) any later
7 // version.
8 //
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 // details.
13 //
14 // You should have received a copy of the GNU General Public License along with
15 // this program; if not, see <http://www.gnu.org/licenses/>.
16 
17 #include <octave/oct.h>
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <octave/utils.h>
24 #ifdef HAVE_OCTAVE_INTERPRETER_H
25 # include <octave/interpreter.h>
26 #endif
27 #include <octave/symtab.h>
28 #include <octave/oct-map.h>
29 
30 #ifdef DEFMETHOD_DLD
31 DEFMETHOD_DLD (unpackfields, interp, args, ,
32 #else
33 DEFUN_DLD (unpackfields, args, ,
34 #endif
35   "-*- texinfo -*-\n\
36 @deftypefn {Loadable Function} {} unpackfields (@var{s_name}, @var{fld1}, @var{fld2}, @dots{})\n\
37 Create variables from fields of a struct.\n\
38 \n\
39 Inserts the named fields @var{fld1}, @var{fld2}, @dots{}, from the struct\n\
40 named @var{s_name}, into the current scope.  Note that @var{s_name} is the\n\
41 name of the struct in the current scope, not the struct itself.\n\
42 \n\
43 @example\n\
44 @group\n\
45 unpackfields (\"struct\", \"var1\", \"var2\")\n\
46 @end group\n\
47 @end example\n\
48 \n\
49 is equivalent to the code:\n\
50 @example\n\
51 @group\n\
52   var1 = struct.var1;\n\
53   var2 = struct.var2;\n\
54           :          \n\
55 @end group\n\
56 @end example\n\
57 but more efficient and more concise.\n\
58 \n\
59 @seealso{getfield, getfields, packfields, struct}\n\
60 @end deftypefn")
61 {
62   int nargin = args.length ();
63 
64   if (nargin > 0)
65     {
66 #ifdef DEFMETHOD_DLD
67 #ifndef OCTAVE_HAVE_INTERPRETER_VARVAL
68       octave::symbol_table::scope curr_scope
69        = interp.require_current_scope ("unpackfields");
70 #endif
71 #endif
72       if (! args (0).is_string ())
73         {
74             error ("unpackfields: expected variable name");
75 	    return octave_value ();
76         }
77 
78       std::string struct_name = args (0).string_value ();
79       string_vector fld_names(nargin-1);
80 
81       if (! OCTAVE__VALID_IDENTIFIER (struct_name))
82         {
83           error ("unpackfields: invalid variable name: %s",
84                  struct_name.c_str ());
85 	    return octave_value ();
86         }
87 
88       for (octave_idx_type i = 0; i < nargin-1; i++)
89         {
90           if (! args (i+1).is_string ())
91             {
92               error ("unpackfields: expected variable name for input %d", (int)i+1);
93 	      return octave_value ();
94             }
95 
96           std::string fld_name = args(i+1).string_value ();
97 
98           if (OCTAVE__VALID_IDENTIFIER (fld_name))
99             fld_names(i) = fld_name;
100           else
101             {
102               error ("unpackfields: invalid field name: %s", fld_name.c_str ());
103               return octave_value ();
104             }
105         }
106 
107       // Force the symbol to be inserted in caller's scope.
108 #ifdef DEFMETHOD_DLD
109 #ifdef OCTAVE_HAVE_INTERPRETER_VARVAL
110       octave_value struct_val = interp.varval (struct_name);
111 #else
112       octave_value struct_val = curr_scope.varval (struct_name);
113 #endif
114 #else
115       octave_value struct_val = symbol_table::varval (struct_name);
116 #endif
117       if (struct_val.OV_ISMAP ())
118         {
119 	  if (struct_val.ndims () == 2)
120             {
121               // Fast code for a built-in struct.
122               const octave_scalar_map map = struct_val.scalar_map_value ();
123 
124               // Do the actual work.
125               for (octave_idx_type i = 0; i < nargin-1; i++)
126                 {
127                   octave_scalar_map::const_iterator iter = map.seek (fld_names(i));
128                   if (iter != map.end ())
129 #ifdef DEFMETHOD_DLD
130 #ifdef OCTAVE_HAVE_INTERPRETER_ASSIGN
131                     interp.assign (fld_names(i), map.contents (iter));
132 #else
133                     curr_scope.assign (fld_names(i), map.contents (iter));
134 #endif
135 #else
136                     symbol_table::assign (fld_names(i), map.contents (iter));
137 #endif
138                   else
139                     {
140                       error ("unpackfields: field %s does not exist", fld_names(i).c_str ());
141                       break;
142                     }
143                 }
144             }
145           else
146             {
147               error ("unpackfields: structure must have singleton dimensions");
148               return octave_value ();
149             }
150         }
151       else if (struct_val.is_defined ())
152         {
153           // General case.
154           std::list<octave_value_list> idx (1);
155 
156           for (octave_idx_type i = 0; i < nargin-1; i++)
157             {
158               idx.front () = args(i+1); // Save one string->octave_value conversion.
159               octave_value val = struct_val.subsref (".", idx);
160 
161               if (val.is_defined ())
162 #ifdef DEFMETHOD_DLD
163 #ifdef OCTAVE_HAVE_INTERPRETER_ASSIGN
164                 interp.assign (fld_names(i), val);
165 #else
166                 curr_scope.assign (fld_names(i), val);
167 #endif
168 #else
169                 symbol_table::assign (fld_names(i), val);
170 #endif
171             }
172         }
173     }
174   else
175     print_usage ();
176 
177   return octave_value_list ();
178 }
179 
180 /*
181 %!test
182 %! s.foo = "hello";
183 %! s.bar = 42;
184 %! unpackfields ("s", "foo", "bar");
185 %! assert (foo, "hello");
186 %! assert (bar, 42);
187 */
188