Skip to content

Commit b8ac5bf

Browse files
committed
Do not treat struct as return argument type for saveobj differently (bug #45833).
* libinterp/octave-value/ov-classdef.cc (octave_classdef::saveobj): Set indicator for a different type of the return argument of an overloaded "saveobj" method anytime the type and class does not match the original type and class of the object that is to be saved. (octave_classdef::loadobj): Adjust for the change in octave_classdef::saveobj. Emit more specific warnings for (currently) unsupported scenarios. * test/classdef-load-save/classdef-load-save.tst: Adjust test for saveobj returning a structure to the actually expected behavior. Add test for saveobj returning an object (of the correct class). * test/classdef-load-save/saveobj_obj_class.m: Add definition for class that returns an object of the correct class from the overloaded "saveobj" method and that does not overload "loadobj". * test/classdef-load-save/saveobj_struct_class.m: Rename from "saveobj_class.m". * test/classdef-load-save/module.mk: Add new and renamed files to dist tarball.
1 parent 9c46a47 commit b8ac5bf

File tree

5 files changed

+83
-19
lines changed

5 files changed

+83
-19
lines changed

libinterp/octave-value/ov-classdef.cc

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,12 @@ octave_classdef::saveobj (std::vector<bool>& is_new)
104104
warning ("save: saveobj method does not return a value");
105105
return m;
106106
}
107-
else if (! (retval.isobject () || retval.isstruct ()))
107+
else if (! retval.is_classdef_object ()
108+
|| retval.class_name () != class_name ())
108109
{
109-
// If retval is not a struct or an object, we put the value
110-
// in a map and set the 'custom_saveobj_ret_type' flag, which
111-
// has to be encoded in the file metadata.
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.
112113
// It's the caller's responsibility to check the flag.
113114
std::get<octave_map> (m[n]).setfield ("any", retval);
114115
std::get<bool> (m[n]) = true;
@@ -171,17 +172,25 @@ octave_classdef::loadobj (std::vector<std::tuple<octave_map, uint32_t, bool>>& m
171172
}
172173

173174
// FIXME: A loadobj method can return any type. If the return
174-
// type is not a classdef object, then the loaded object
175-
// must be replaced by whatever the return type and
176-
// contents are.
177-
if (! ov.isobject ())
175+
// type is not a classdef object of the correct class,
176+
// then the loaded object must be replaced by whatever the
177+
// return type and contents are.
178+
if (! ov.is_classdef_object ())
178179
{
179180
std::string type = ov.type_name ();
180-
warning ("load: loadobj method return non-classdef return type '%s'. "
181-
"This is currently not supported.",
181+
warning ("load: loadobj method does not return correct type "
182+
"'%s'. This is currently not supported.",
182183
type.c_str ());
183184
return;
184185
}
186+
else if (ov.class_name () != class_name ())
187+
{
188+
std::string class_nm = ov.class_name ();
189+
warning ("load: loadobj method does not return classdef object "
190+
"of correct class '%s'. This is currently not supported.",
191+
class_nm.c_str ());
192+
return;
193+
}
185194

186195
if (in_obj_cache)
187196
// Copy the results from the loadobj methods to the object in
@@ -192,12 +201,42 @@ octave_classdef::loadobj (std::vector<std::tuple<octave_map, uint32_t, bool>>& m
192201
}
193202
else
194203
{
195-
// If custom saveobj is implemented, then a variable named 'any'
196-
// is meant to be passed to loadobj, but if loadobj is not
197-
// implemented, it should not fill in any property 'any' in the
198-
// class.
199204
if (std::get<bool> (m[n]))
200-
return;
205+
{
206+
// If saveobj is overloaded by this classdef and it returned
207+
// anything other than a classdef object of the correct
208+
// class, then a variable named 'any' is meant to be passed
209+
// to loadobj, but if loadobj is not overloaded, it should
210+
// not fill in any property 'any' in the loaded object.
211+
if (! prop_map.isfield ("any") || prop_map.numel () != 1)
212+
{
213+
warning ("load: expected scalar value for custom type when loading object");
214+
return;
215+
}
216+
217+
// FIXME: What should be done here?
218+
octave_value any_val = (prop_map.getfield ("any"))(0);
219+
std::string type = any_val.type_name ();
220+
if (type != type_name ())
221+
{
222+
warning ("load: cannot restore value of object that was saved as a different type (%s)",
223+
type.c_str ());
224+
return;
225+
}
226+
227+
std::string cls_nm = any_val.class_name ();
228+
if (cls_nm != class_name ())
229+
{
230+
warning ("load: cannot restore value of object that was saved as a different class (%s)",
231+
cls_nm.c_str ());
232+
return;
233+
}
234+
235+
// If the value in the "any" field has the correct type and
236+
// class, we can load it like it were saved "normally".
237+
// FIXME: Can this ever happen?
238+
prop_map = any_val.classdef_object_value ()->map_value (false, true);
239+
}
201240

202241
octave::cdef_object new_object;
203242
if (in_obj_cache)

test/classdef-load-save/classdef-load-save.tst

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,33 @@
110110
%! delete (savefile);
111111
%! end_unwind_protect
112112

113+
## No constructor, ConstructOnLoad = false, saveobj returns an object, no loadobj
114+
%!test
115+
%! obj = saveobj_obj_class ();
116+
%! obj.a = 1;
117+
%! obj.b = 3;
118+
%! savefile = tempname ();
119+
%! save ('-text', savefile, 'obj');
120+
%! unwind_protect
121+
%! clear obj;
122+
%! load (savefile);
123+
%! assert(obj.a, 2);
124+
%! assert(obj.b, 3);
125+
%! assert(obj.c, []);
126+
%! unwind_protect_cleanup
127+
%! delete (savefile);
128+
%! end_unwind_protect
129+
113130
## No constructor, ConstructOnLoad = false, saveobj returns a struct, no loadobj
114131
%!test
115-
%! obj = saveobj_class ();
132+
%! obj = saveobj_struct_class ();
116133
%! obj.a = 1;
117134
%! savefile = tempname ();
118135
%! save ('-text', savefile, 'obj');
119136
%! unwind_protect
120137
%! clear obj;
121138
%! load (savefile);
122-
%! assert(obj.a, 1);
139+
%! assert(obj.a, []);
123140
%! assert(obj.b, []);
124141
%! unwind_protect_cleanup
125142
%! delete (savefile);

test/classdef-load-save/module.mk

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ classdef_load_save_TEST_FILES = \
88
%reldir%/regular_class_construct_on_load.m \
99
%reldir%/regular_class_with_constructor.m \
1010
%reldir%/regular_handle_class.m \
11-
%reldir%/saveobj_class.m \
11+
%reldir%/saveobj_obj_class.m \
12+
%reldir%/saveobj_struct_class.m \
1213
%reldir%/transient_property_class.m
1314

1415
TEST_FILES += $(classdef_load_save_TEST_FILES)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
classdef saveobj_obj_class < regular_class
2+
methods
3+
function obj = saveobj (obj)
4+
obj.a = 2;
5+
endfunction
6+
endmethods
7+
endclassdef

test/classdef-load-save/saveobj_class.m renamed to test/classdef-load-save/saveobj_struct_class.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
classdef saveobj_class < regular_class
1+
classdef saveobj_struct_class < regular_class
22
methods
33
function s = saveobj (obj)
44
s.a = obj.a;

0 commit comments

Comments
 (0)