-
Notifications
You must be signed in to change notification settings - Fork 468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Add infrastructure for state serialization #488
base: master
Are you sure you want to change the base?
Changes from all commits
3ea0cf1
8b500bb
72b0428
85b9041
ea13f91
3b9d20c
e904d0a
3f553e5
bc063a7
5e962bf
ae4deaa
ba07766
272d315
c41b785
59ffb36
be5c858
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -766,7 +766,7 @@ class Measure_<T>::Variable::Implementation | |
} | ||
T& updVarValue(State& s) const { | ||
assert(discreteVarIndex.isValid()); | ||
return Value<T>::downcast( | ||
return Value<T>::updDowncast( | ||
this->getSubsystem().updDiscreteVariable(s, discreteVarIndex)); | ||
} | ||
|
||
|
@@ -1289,6 +1289,43 @@ template <class T> | |
class Measure_Differentiate_Result { | ||
public: | ||
Measure_Differentiate_Result() : derivIsGood(false) {} | ||
|
||
Xml::Element toXmlElement(const std::string& name) const { | ||
static const int version = 1; | ||
Xml::Element e("Measure_Differentiate_Result"); | ||
if (!name.empty()) e.setAttributeValue("name", name); | ||
e.setAttributeValue("version", String(version)); | ||
e.appendNode(toXmlElementHelper(operand, "operand", true)); | ||
e.appendNode(toXmlElementHelper(operandDot, "operandDot", true)); | ||
e.appendNode(toXmlElementHelper(derivIsGood, "derivIsGood", true)); | ||
return e; | ||
} | ||
|
||
void fromXmlElement(Xml::Element e, | ||
const std::string& requiredName="") { | ||
const int versionInXml = e.getRequiredAttributeValueAs<int>("version"); | ||
SimTK_ERRCHK1_ALWAYS(versionInXml == 1, | ||
"Measure_Differentiate_Result::fromXmlElement", | ||
"Expected Measure_Differentiate_Result version=1 but got %d.", | ||
versionInXml); | ||
if (!requiredName.empty()) { | ||
const String& name = e.getElementTag(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm...after looking at some more of the code, it seems there are two scenarios, one where the name is the tag name, and one where it's the value of the name attribute. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think that's right. That's because I made it optional to create a unique tag for serializing user objects. If the "to" method created a special tag, then the "from" method can look for it. Not sure that was a good idea, but you don't want to have to make up tags for everything (int, char, etc.). They really aren't needed because we always know what type we're expecting by the time we read it in, except for |
||
SimTK_ERRCHK2_ALWAYS(name==requiredName, | ||
"Measure_Differentiate_Result::fromXmlElement", | ||
"Expected Measure_Differentiate_Result element " | ||
"named '%s' but got '%s'.", requiredName.c_str(), name.c_str()); | ||
} | ||
auto nxt = e.element_begin(); | ||
fromXmlElementHelper(operand, *nxt++, "operand", true); | ||
fromXmlElementHelper(operandDot, *nxt++, "operandDot", true); | ||
fromXmlElementHelper(derivIsGood, *nxt++, "derivIsGood", true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not very friendly! |
||
|
||
SimTK_ERRCHK_ALWAYS(nxt == e.element_end(), | ||
"Measure_Differentiate_Result::fromXmlElement", | ||
"Badly formatted Measure_Differentiate_Result element: too many " | ||
"sub-elements."); | ||
} | ||
|
||
T operand; // previous value of operand | ||
T operandDot; // previous value of derivative | ||
bool derivIsGood; // do we think the deriv is a good one? | ||
|
@@ -1905,6 +1942,18 @@ class Measure_Delay_Buffer { | |
// Return the largest capacity the buffer ever had. | ||
int getMaxCapacity() const {return m_maxCapacity;} | ||
|
||
Xml::Element toXmlElement(const std::string& name) const { | ||
static const int version = 1; | ||
Xml::Element e("Measure_Delay_Buffer"); | ||
if (!name.empty()) e.setAttributeValue("name", name); | ||
e.setAttributeValue("version", String(version)); | ||
for (int i=0; i < size(); ++i) { | ||
e.appendNode(toXmlElementHelper(getEntryTime(i), "time", true)); | ||
e.appendNode(toXmlElementHelper(getEntryValue(i), "value", true)); | ||
} | ||
return e; | ||
} | ||
|
||
private: | ||
// Return the i'th oldest entry | ||
// (0 -> oldest, size-1 -> newest, size -> first free, -1 -> last free) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that
toXmlElementHelper
has to be used here. Would be friendlier for people writing these methods if it were justtoXmlElement
and the mysterious third parameter were hidden somewhere below.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify, you don't like that
toXmlElementHelper
has to be used anywhere (withintoXmlElement()
methods), right? Can you educate me as to why the third parameter is necessary for SFINAE?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the "Helper" method should just be needed in the internal implementation. Here it is appearing in user-written code for serializing.
The issue is that we normally want classes to have
toXmlElement()
methods. But when serializing an existing class (std::vector
, say) you can't add a new method. So you have to write a free functiontoXmlElement(std::vector<T>)
to serialize it. What if there is both a member namedMyClass::toXmlElement()
and a free functiontoXmlElement(MyClass)
? I wanted to ensure that the member gets called. By template resolution rules, thattrue
value is a better match for abool
than anint
. So I arranged things so that the member function gets called from a Helper that takes abool
while the free function gets called from a Helper that takes anint
. That one gets called if there's nobool
competing with it becausetrue
gets promoted toint
.All that is still necessary; I'm just thinking it doesn't have to appear in user code. Use code could call some nicely-named method, then that method could invoke the Helper which would then pick out the right member or free function. I think that should be possible ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you; that was very helpful. I also described this situation to @klshrinidhi, who may be interested in helping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any other idea you guys have for the API would be welcome. It should be as straightforward as possible for someone to write serialization/deserialization methods for any class they want to stick into an AbstractValue in a State. I assume people will do it by copying from existing examples so it should be very predictable. The current API might be OK but it doesn't seem optimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @klshrinidhi. I think this prioritizes the free function over the member though, which is opposite of what we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It chooses the specialization over member function.
I think it makes sense because if I put the effort to specialize the function to change the serialization behavior, I would like it to be called instead of member function. Choosing the member function even when a specialization exists defeats the purpose of specializing. The person who specialized the function will be surprised to see it not take effect.
Again, I am not too familiar with this development. So you may be right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@klshrinidhi and I just had a whiteboard session and he brought up two other points:
toXmlElement()
) over a member function) if both exist. Why would one define the free function if the class already hadtoXmlElement()
/fromXmlElement()
? Perhaps the only reason would be because they want to override the behavior of the member function to achieve something new. Probable answer: the control of serializing a type should be held by that type; it would be dangerous for others to subsume that control. The resulting Xml file could not be loaded correctly by others who did not have that free function but were using the same class.std::begin()
andstd::end()
, @klshrinidhi was saying how you can callbegin()
orend()
(without any namespace qualification), and if the argument is in thestd
namespace thenstd::begin()
/std::end()
will be called. He raised the idea that it could be nice to never have to qualifytoXmlElement()
(ortoXmlElementHelper()
) withSimTK::
. This would require a<ns>::toXmlElement()
in each namespace that wanted to use it, though.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yikes! Who knows? Some of the proposals would make this easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving discussion out of the code ...