GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
jsondecode.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2020-2024 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 "defun.h"
31 #include "error.h"
32 #include "errwarn.h"
33 #include "oct-string.h"
34 #include "ovl.h"
35 #include "utils.h"
36 
37 #if defined (HAVE_RAPIDJSON)
38 # include <rapidjson/document.h>
39 # include <rapidjson/error/en.h>
40 #endif
41 
43 
44 #if defined (HAVE_RAPIDJSON)
45 
46 static octave_value
47 decode (const rapidjson::Value& val,
48  const octave::make_valid_name_options *options);
49 
50 //! Decodes a numerical JSON value into a scalar number.
51 //!
52 //! @param val JSON value that is guaranteed to be a numerical value.
53 //!
54 //! @return @ref octave_value that contains the numerical value of @p val.
55 //!
56 //! @b Example:
57 //!
58 //! @code{.cc}
59 //! rapidjson::Document d;
60 //! d.Parse ("123");
61 //! octave_value num = decode_number (d);
62 //! @endcode
63 
64 static octave_value
65 decode_number (const rapidjson::Value& val)
66 {
67  if (val.IsUint ())
68  return octave_value (val.GetUint ());
69  else if (val.IsInt ())
70  return octave_value (val.GetInt ());
71  else if (val.IsUint64 ())
72  return octave_value (val.GetUint64 ());
73  else if (val.IsInt64 ())
74  return octave_value (val.GetInt64 ());
75  else if (val.IsDouble ())
76  return octave_value (val.GetDouble ());
77  else
78  error ("jsondecode: unidentified type");
79 }
80 
81 //! Decodes a JSON object into a scalar struct.
82 //!
83 //! @param val JSON value that is guaranteed to be a JSON object.
84 //! @param options @c ReplacementStyle and @c Prefix options with their values.
85 //!
86 //! @return @ref octave_value that contains the equivalent scalar struct of @p val.
87 //!
88 //! @b Example:
89 //!
90 //! @code{.cc}
91 //! rapidjson::Document d;
92 //! d.Parse ("{\"a\": 1, \"b\": 2}");
93 //! octave_value struct = decode_object (d, octave_value_list ());
94 //! @endcode
95 
96 static octave_value
97 decode_object (const rapidjson::Value& val,
98  const octave::make_valid_name_options *options)
99 {
100  octave_scalar_map retval;
101 
102  for (const auto& pair : val.GetObject ())
103  {
104  // Validator function "matlab.lang.makeValidName" to guarantee legitimate
105  // variable name.
106  std::string varname = pair.name.GetString ();
107  if (options != nullptr)
108  octave::make_valid_name (varname, *options);
109  retval.assign (varname, decode (pair.value, options));
110  }
111 
112  return retval;
113 }
114 
115 //! Decodes a JSON array that contains only numerical or null values
116 //! into an NDArray.
117 //!
118 //! @param val JSON value that is guaranteed to be a numeric array.
119 //!
120 //! @return @ref octave_value that contains the equivalent NDArray of @p val.
121 //!
122 //! @b Example:
123 //!
124 //! @code{.cc}
125 //! rapidjson::Document d;
126 //! d.Parse ("[1, 2, 3, 4]");
127 //! octave_value numeric_array = decode_numeric_array (d);
128 //! @endcode
129 
130 static octave_value
131 decode_numeric_array (const rapidjson::Value& val)
132 {
133  NDArray retval (dim_vector (val.Size (), 1));
134  octave_idx_type index = 0;
135  for (const auto& elem : val.GetArray ())
136  retval(index++) = elem.IsNull () ? octave_NaN
137  : decode_number (elem).double_value ();
138  return retval;
139 }
140 
141 //! Decodes a JSON array that contains only boolean values into a boolNDArray.
142 //!
143 //! @param val JSON value that is guaranteed to be a boolean array.
144 //!
145 //! @return @ref octave_value that contains the equivalent boolNDArray of @p val.
146 //!
147 //! @b Example:
148 //!
149 //! @code{.cc}
150 //! rapidjson::Document d;
151 //! d.Parse ("[true, false, true]");
152 //! octave_value boolean_array = decode_boolean_array (d);
153 //! @endcode
154 
155 static octave_value
156 decode_boolean_array (const rapidjson::Value& val)
157 {
158  boolNDArray retval (dim_vector (val.Size (), 1));
159  octave_idx_type index = 0;
160  for (const auto& elem : val.GetArray ())
161  retval(index++) = elem.GetBool ();
162  return retval;
163 }
164 
165 //! Decodes a JSON array that contains different types
166 //! or string values only into a Cell.
167 //!
168 //! @param val JSON value that is guaranteed to be a mixed or string array.
169 //! @param options @c ReplacementStyle and @c Prefix options with their values.
170 //!
171 //! @return @ref octave_value that contains the equivalent Cell of @p val.
172 //!
173 //! @b Example (decoding a string array):
174 //!
175 //! @code{.cc}
176 //! rapidjson::Document d;
177 //! d.Parse ("[\"foo\", \"bar\", \"baz\"]");
178 //! octave_value cell = decode_string_and_mixed_array (d, octave_value_list ());
179 //! @endcode
180 //!
181 //! @b Example (decoding a mixed array):
182 //!
183 //! @code{.cc}
184 //! rapidjson::Document d;
185 //! d.Parse ("[\"foo\", 123, true]");
186 //! octave_value cell = decode_string_and_mixed_array (d, octave_value_list ());
187 //! @endcode
188 
189 static octave_value
190 decode_string_and_mixed_array (const rapidjson::Value& val,
191  const octave::make_valid_name_options *options)
192 {
193  Cell retval (dim_vector (val.Size (), 1));
194  octave_idx_type index = 0;
195  for (const auto& elem : val.GetArray ())
196  retval(index++) = decode (elem, options);
197  return retval;
198 }
199 
200 //! Decodes a JSON array that contains only objects into a Cell or struct array
201 //! depending on the similarity of the objects' keys.
202 //!
203 //! @param val JSON value that is guaranteed to be an object array.
204 //! @param options @c ReplacementStyle and @c Prefix options with their values.
205 //!
206 //! @return @ref octave_value that contains the equivalent Cell
207 //! or struct array of @p val.
208 //!
209 //! @b Example (returns a struct array):
210 //!
211 //! @code{.cc}
212 //! rapidjson::Document d;
213 //! d.Parse ("[{\"a\":1,\"b\":2},{\"a\":3,\"b\":4}]");
214 //! octave_value object_array = decode_object_array (d, octave_value_list ());
215 //! @endcode
216 //!
217 //! @b Example (returns a Cell):
218 //!
219 //! @code{.cc}
220 //! rapidjson::Document d;
221 //! d.Parse ("[{\"a\":1,\"b\":2},{\"b\":3,\"a\":4}]");
222 //! octave_value object_array = decode_object_array (d, octave_value_list ());
223 //! @endcode
224 
225 static octave_value
226 decode_object_array (const rapidjson::Value& val,
227  const octave::make_valid_name_options *options)
228 {
229  Cell struct_cell = decode_string_and_mixed_array (val, options).cell_value ();
230  string_vector field_names = struct_cell(0).scalar_map_value ().fieldnames ();
231 
232  bool same_field_names = true;
233  for (octave_idx_type i = 1; i < struct_cell.numel (); ++i)
234  if (field_names.std_list ()
235  != struct_cell(i).scalar_map_value ().fieldnames ().std_list ())
236  {
237  same_field_names = false;
238  break;
239  }
240 
241  if (same_field_names)
242  {
243  octave_map struct_array;
244  dim_vector struct_array_dims = dim_vector (struct_cell.numel (), 1);
245 
246  if (field_names.numel ())
247  {
248  Cell value (struct_array_dims);
249  for (octave_idx_type i = 0; i < field_names.numel (); ++i)
250  {
251  for (octave_idx_type k = 0; k < struct_cell.numel (); ++k)
252  value(k) = struct_cell(k).scalar_map_value ()
253  .getfield (field_names(i));
254  struct_array.assign (field_names(i), value);
255  }
256  }
257  else
258  struct_array.resize (struct_array_dims, true);
259 
260  return struct_array;
261  }
262  else
263  return struct_cell;
264 }
265 
266 //! Decodes a JSON array that contains only arrays into a Cell or an NDArray
267 //! depending on the dimensions and element types of the sub-arrays.
268 //!
269 //! @param val JSON value that is guaranteed to be an array of arrays.
270 //! @param options @c ReplacementStyle and @c Prefix options with their values.
271 //!
272 //! @return @ref octave_value that contains the equivalent Cell
273 //! or NDArray of @p val.
274 //!
275 //! @b Example (returns an NDArray):
276 //!
277 //! @code{.cc}
278 //! rapidjson::Document d;
279 //! d.Parse ("[[1, 2], [3, 4]]");
280 //! octave_value array = decode_array_of_arrays (d, octave_value_list ());
281 //! @endcode
282 //!
283 //! @b Example (returns a Cell):
284 //!
285 //! @code{.cc}
286 //! rapidjson::Document d;
287 //! d.Parse ("[[1, 2], [3, 4, 5]]");
288 //! octave_value cell = decode_array_of_arrays (d, octave_value_list ());
289 //! @endcode
290 
291 static octave_value
292 decode_array_of_arrays (const rapidjson::Value& val,
293  const octave::make_valid_name_options *options)
294 {
295  // Some arrays should be decoded as NDArrays and others as cell arrays
296  Cell cell = decode_string_and_mixed_array (val, options).cell_value ();
297 
298  // Only arrays with sub-arrays of booleans and numericals will return NDArray
299  bool is_bool = cell(0).is_bool_matrix ();
300  bool is_struct = cell(0).isstruct ();
301  string_vector field_names = is_struct ? cell(0).map_value ().fieldnames ()
302  : string_vector ();
303  dim_vector sub_array_dims = cell(0).dims ();
304  octave_idx_type sub_array_ndims = cell(0).ndims ();
305  octave_idx_type cell_numel = cell.numel ();
306  for (octave_idx_type i = 0; i < cell_numel; ++i)
307  {
308  // If one element is cell return the cell array as at least one of the
309  // sub-arrays area either an array of: strings, objects or mixed array
310  if (cell(i).iscell ())
311  return cell;
312  // If not the same dim of elements or dim = 0, return cell array
313  if (cell(i).dims () != sub_array_dims || sub_array_dims == dim_vector ())
314  return cell;
315  // If not numeric sub-arrays only or bool sub-arrays only,
316  // return cell array
317  if (cell(i).is_bool_matrix () != is_bool)
318  return cell;
319  // If not struct arrays only, return cell array
320  if (cell(i).isstruct () != is_struct)
321  return cell;
322  // If struct arrays have different fields, return cell array
323  if (is_struct && (field_names.std_list ()
324  != cell(i).map_value ().fieldnames ().std_list ()))
325  return cell;
326  }
327 
328  // Calculate the dims of the output array
329  dim_vector array_dims;
330  array_dims.resize (sub_array_ndims + 1);
331  array_dims(0) = cell_numel;
332  for (auto i = 1; i < sub_array_ndims + 1; i++)
333  array_dims(i) = sub_array_dims(i-1);
334 
335  if (is_struct)
336  {
337  octave_map struct_array;
338  array_dims.chop_trailing_singletons ();
339 
340  if (field_names.numel ())
341  {
342  Cell value (array_dims);
343  octave_idx_type sub_array_numel = sub_array_dims.numel ();
344 
345  for (octave_idx_type j = 0; j < field_names.numel (); ++j)
346  {
347  // Populate the array with specific order to generate
348  // MATLAB-identical output.
349  for (octave_idx_type k = 0; k < cell_numel; ++k)
350  {
351  Cell sub_array_value = cell(k).map_value ()
352  .getfield (field_names(j));
353  for (octave_idx_type i = 0; i < sub_array_numel; ++i)
354  value(k + i * cell_numel) = sub_array_value(i);
355  }
356  struct_array.assign (field_names(j), value);
357  }
358  }
359  else
360  struct_array.resize(array_dims, true);
361 
362  return struct_array;
363  }
364  else
365  {
366  NDArray array (array_dims);
367 
368  // Populate the array with specific order to generate MATLAB-identical
369  // output.
370  octave_idx_type sub_array_numel = array.numel () / cell_numel;
371  for (octave_idx_type k = 0; k < cell_numel; ++k)
372  {
373  NDArray sub_array_value = cell(k).array_value ();
374  for (octave_idx_type i = 0; i < sub_array_numel; ++i)
375  array(k + i * cell_numel) = sub_array_value(i);
376  }
377 
378  if (is_bool)
379  return boolNDArray (array);
380  else
381  return array;
382  }
383 }
384 
385 //! Decodes any type of JSON arrays. This function only serves as an interface
386 //! by choosing which function to call from the previous functions.
387 //!
388 //! @param val JSON value that is guaranteed to be an array.
389 //! @param options @c ReplacementStyle and @c Prefix options with their values.
390 //!
391 //! @return @ref octave_value that contains the output of decoding @p val.
392 //!
393 //! @b Example:
394 //!
395 //! @code{.cc}
396 //! rapidjson::Document d;
397 //! d.Parse ("[[1, 2], [3, 4, 5]]");
398 //! octave_value array = decode_array (d, octave_value_list ());
399 //! @endcode
400 
401 static octave_value
402 decode_array (const rapidjson::Value& val,
403  const octave::make_valid_name_options *options)
404 {
405  // Handle empty arrays
406  if (val.Empty ())
407  return NDArray ();
408 
409  // Compare with other elements to know if the array has multiple types
410  rapidjson::Type array_type = val[0].GetType ();
411  // Check if the array is numeric and if it has multiple types
412  bool same_type = true;
413  bool is_numeric = true;
414  for (const auto& elem : val.GetArray ())
415  {
416  rapidjson::Type current_elem_type = elem.GetType ();
417  if (is_numeric && ! (current_elem_type == rapidjson::kNullType
418  || current_elem_type == rapidjson::kNumberType))
419  is_numeric = false;
420  if (same_type && (current_elem_type != array_type))
421  // RapidJSON doesn't have kBoolean Type it has kTrueType and kFalseType
422  if (! ((current_elem_type == rapidjson::kTrueType
423  && array_type == rapidjson::kFalseType)
424  || (current_elem_type == rapidjson::kFalseType
425  && array_type == rapidjson::kTrueType)))
426  same_type = false;
427  }
428 
429  if (is_numeric)
430  return decode_numeric_array (val);
431 
432  if (same_type && (array_type != rapidjson::kStringType))
433  {
434  if (array_type == rapidjson::kTrueType
435  || array_type == rapidjson::kFalseType)
436  return decode_boolean_array (val);
437  else if (array_type == rapidjson::kObjectType)
438  return decode_object_array (val, options);
439  else if (array_type == rapidjson::kArrayType)
440  return decode_array_of_arrays (val, options);
441  else
442  error ("jsondecode: unidentified type");
443  }
444  else
445  return decode_string_and_mixed_array (val, options);
446 }
447 
448 //! Decodes any JSON value. This function only serves as an interface
449 //! by choosing which function to call from the previous functions.
450 //!
451 //! @param val JSON value.
452 //! @param options @c ReplacementStyle and @c Prefix options with their values.
453 //!
454 //! @return @ref octave_value that contains the output of decoding @p val.
455 //!
456 //! @b Example:
457 //!
458 //! @code{.cc}
459 //! rapidjson::Document d;
460 //! d.Parse ("[{\"a\":1,\"b\":2},{\"b\":3,\"a\":4}]");
461 //! octave_value value = decode (d, octave_value_list ());
462 //! @endcode
463 
464 static octave_value
465 decode (const rapidjson::Value& val,
466  const octave::make_valid_name_options *options)
467 {
468  if (val.IsBool ())
469  return val.GetBool ();
470  else if (val.IsNumber ())
471  return decode_number (val);
472  else if (val.IsString ())
473  return val.GetString ();
474  else if (val.IsObject ())
475  return decode_object (val, options);
476  else if (val.IsNull ())
477  return NDArray ();
478  else if (val.IsArray ())
479  return decode_array (val, options);
480  else
481  error ("jsondecode: unidentified type");
482 }
483 
484 #endif
485 
486 DEFUN (jsondecode, args, ,
487  doc: /* -*- texinfo -*-
488 @deftypefn {} {@var{object} =} jsondecode (@var{JSON_txt})
489 @deftypefnx {} {@var{object} =} jsondecode (@dots{}, "ReplacementStyle", @var{rs})
490 @deftypefnx {} {@var{object} =} jsondecode (@dots{}, "Prefix", @var{pfx})
491 @deftypefnx {} {@var{object} =} jsondecode (@dots{}, "makeValidName", @var{TF})
492 
493 Decode text that is formatted in JSON.
494 
495 The input @var{JSON_txt} is a string that contains JSON text.
496 
497 The output @var{object} is an Octave object that contains the result of
498 decoding @var{JSON_txt}.
499 
500 For more information about the options @qcode{"ReplacementStyle"} and
501 @qcode{"Prefix"},
502 @pxref{XREFmatlab_lang_makeValidName,,@code{matlab.lang.makeValidName}}.
503 
504 If the value of the option @qcode{"makeValidName"} is false then names
505 will not be changed by @code{matlab.lang.makeValidName} and the
506 @qcode{"ReplacementStyle"} and @qcode{"Prefix"} options will be ignored.
507 
508 NOTE: Decoding and encoding JSON text is not guaranteed to reproduce the
509 original text as some names may be changed by @code{matlab.lang.makeValidName}.
510 
511 This table shows the conversions from JSON data types to Octave data types:
512 
513 @multitable @columnfractions 0.50 0.50
514 @headitem JSON data type @tab Octave data type
515 @item Boolean @tab scalar logical
516 @item Number @tab scalar double
517 @item String @tab vector of characters
518 @item Object @tab scalar struct (field names of the struct may be different from the keys of the JSON object due to @code{matlab_lang_makeValidName}
519 @item null, inside a numeric array @tab @code{NaN}
520 @item null, inside a non-numeric array @tab empty double array @code{[]}
521 @item Array, of different data types @tab cell array
522 @item Array, of Booleans @tab logical array
523 @item Array, of Numbers @tab double array
524 @item Array, of Strings @tab cell array of character vectors (@code{cellstr})
525 @item Array of Objects, same field names @tab struct array
526 @item Array of Objects, different field names @tab cell array of scalar structs
527 @end multitable
528 
529 Examples:
530 
531 @example
532 @group
533 jsondecode ('[1, 2, null, 3]')
534  @result{} ans =
535 
536  1
537  2
538  NaN
539  3
540 @end group
541 
542 @group
543 jsondecode ('["foo", "bar", ["foo", "bar"]]')
544  @result{} ans =
545  @{
546  [1,1] = foo
547  [2,1] = bar
548  [3,1] =
549  @{
550  [1,1] = foo
551  [2,1] = bar
552  @}
553 
554  @}
555 @end group
556 
557 @group
558 jsondecode ('@{"nu#m#ber": 7, "s#tr#ing": "hi"@}', ...
559  'ReplacementStyle', 'delete')
560  @result{} scalar structure containing the fields:
561 
562  number = 7
563  string = hi
564 @end group
565 
566 @group
567 jsondecode ('@{"nu#m#ber": 7, "s#tr#ing": "hi"@}', ...
568  'makeValidName', false)
569  @result{} scalar structure containing the fields:
570 
571  nu#m#ber = 7
572  s#tr#ing = hi
573 @end group
574 
575 @group
576 jsondecode ('@{"1": "one", "2": "two"@}', 'Prefix', 'm_')
577  @result{} scalar structure containing the fields:
578 
579  m_1 = one
580  m_2 = two
581 @end group
582 @end example
583 
584 @seealso{jsonencode, matlab.lang.makeValidName}
585 @end deftypefn */)
586 {
587 #if defined (HAVE_RAPIDJSON)
588 
589  int nargin = args.length ();
590 
591  // makeValidName options are pairs, the number of arguments must be odd.
592  if (! (nargin % 2))
593  print_usage ();
594 
595  // Detect if the user wants to use makeValidName
596  bool use_makeValidName = true;
597  octave_value_list make_valid_name_params;
598  for (auto i = 1; i < nargin; i = i + 2)
599  {
600  std::string parameter = args(i).xstring_value ("jsondecode: "
601  "option argument must be a string");
602  if (string::strcmpi (parameter, "makeValidName"))
603  {
604  use_makeValidName = args(i + 1).xbool_value ("jsondecode: "
605  "'makeValidName' value must be a bool");
606  }
607  else
608  make_valid_name_params.append (args.slice(i, 2));
609  }
610 
611  make_valid_name_options *options
612  = use_makeValidName ? new make_valid_name_options (make_valid_name_params)
613  : nullptr;
614 
615  unwind_action del_opts ([options] () { if (options) delete options; });
616 
617  if (! args(0).is_string ())
618  error ("jsondecode: JSON_TXT must be a character string");
619 
620  std::string json = args(0).string_value ();
621  rapidjson::Document d;
622  // DOM is chosen instead of SAX as SAX publishes events to a handler that
623  // decides what to do depending on the event only. This will cause a
624  // problem in decoding JSON arrays as the output may be an array or a cell
625  // and that doesn't only depend on the event (startArray) but also on the
626  // types of the elements inside the array.
627  d.Parse <rapidjson::kParseNanAndInfFlag> (json.c_str ());
628 
629  if (d.HasParseError ())
630  error ("jsondecode: parse error at offset %u: %s\n",
631  static_cast<unsigned int> (d.GetErrorOffset ()) + 1,
632  rapidjson::GetParseError_En (d.GetParseError ()));
633 
634  return decode (d, options);
635 
636 #else
637 
638  octave_unused_parameter (args);
639 
640  err_disabled_feature ("jsondecode", "JSON decoding through RapidJSON");
641 
642 #endif
643 }
644 
645 /*
646 Functional BIST tests are located in test/json/jsondecode_BIST.tst
647 
648 ## Input validation tests
649 %!testif HAVE_RAPIDJSON
650 %! fail ("jsondecode ()");
651 %! fail ("jsondecode ('1', 2)");
652 %! fail ("jsondecode (1)", "JSON_TXT must be a character string");
653 %! fail ("jsondecode ('12-')", "parse error at offset 3");
654 
655 */
656 
657 OCTAVE_END_NAMESPACE(octave)
int ndims() const
Size of the specified dimension.
Definition: Array.h:671
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:503
octave_idx_type numel() const
Number of elements in the array.
Definition: Array.h:414
Definition: Cell.h:43
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:94
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:335
void chop_trailing_singletons()
Definition: dim-vector.h:164
void resize(int n, int fill_value=0)
Definition: dim-vector.h:272
Helper class for make_valid_name function calls.
Definition: utils.h:57
void resize(const dim_vector &dv, bool fill=false)
Definition: oct-map.cc:574
void assign(const std::string &k, const Cell &val)
Definition: oct-map.h:344
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:230
octave_value_list & append(const octave_value &val)
Definition: ovl.cc:98
octave_idx_type numel() const
Definition: str-vec.h:100
std::list< std::string > std_list() const
Definition: str-vec.cc:172
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void() error(const char *fmt,...)
Definition: error.cc:988
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
#define octave_NaN
Definition: lo-ieee.h:46
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
bool make_valid_name(std::string &str, const make_valid_name_options &options)
Definition: utils.cc:137
bool strcmpi(const T &str_a, const T &str_b)
True if strings are the same, ignoring case.
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))