Throw an error if a child attached by reference to a parent is compiled.
Fixes #2482. PiperOrigin-RevId: 735665098 Change-Id: Ia3436fdd2ff572f8f0c1ded434742a4a9186f2f7
This commit is contained in:
committed by
Copybara-Service
parent
14dc6fd33b
commit
019e0a806c
@@ -147,8 +147,10 @@ or :ref:`attach a frame to a body<mjs_attachFrame>`:
|
|||||||
|
|
||||||
Note that in the above examples, the parent and child models have different values for ``compiler.degree``,
|
Note that in the above examples, the parent and child models have different values for ``compiler.degree``,
|
||||||
corresponding to the :ref:`compiler/angle<compiler-angle>` attribute, specifying the units in which angles are
|
corresponding to the :ref:`compiler/angle<compiler-angle>` attribute, specifying the units in which angles are
|
||||||
interperted. Compiler options are carried over during attachment, so the child model will be compiled using X, while the
|
interperted. Compiler flags are carried over during attachment, so the child model will be compiled using the child
|
||||||
parent will be compiled using Y.
|
flags, while the parent will be compiled using the parent flags.
|
||||||
|
|
||||||
|
Note also that once a child is attached by reference to a parent, the child cannot be compiled on its own.
|
||||||
|
|
||||||
.. _meDefault:
|
.. _meDefault:
|
||||||
|
|
||||||
|
|||||||
@@ -3972,6 +3972,9 @@ mjModel* mjCModel::Compile(const mjVFS* vfs, mjModel** m) {
|
|||||||
warningtext[0] = 0;
|
warningtext[0] = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (attached_) {
|
||||||
|
throw mjCError(0, "cannot compile child spec if attached by reference to a parent spec");
|
||||||
|
}
|
||||||
if (setjmp(error_jmp_buf) != 0) {
|
if (setjmp(error_jmp_buf) != 0) {
|
||||||
// TryCompile resulted in an mju_error which was converted to a longjmp.
|
// TryCompile resulted in an mju_error which was converted to a longjmp.
|
||||||
std::string error_msg = errortext;
|
std::string error_msg = errortext;
|
||||||
|
|||||||
@@ -317,6 +317,9 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
// set deepcopy flag
|
// set deepcopy flag
|
||||||
void SetDeepCopy(bool deepcopy) { deepcopy_ = deepcopy; }
|
void SetDeepCopy(bool deepcopy) { deepcopy_ = deepcopy; }
|
||||||
|
|
||||||
|
// set attached flag
|
||||||
|
void SetAttached(bool deepcopy) { attached_ |= !deepcopy; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// settings for each defaults class
|
// settings for each defaults class
|
||||||
std::vector<mjCDef*> defaults_;
|
std::vector<mjCDef*> defaults_;
|
||||||
@@ -428,5 +431,6 @@ class mjCModel : public mjCModel_, private mjSpec {
|
|||||||
mjCError errInfo; // last error info
|
mjCError errInfo; // last error info
|
||||||
std::vector<mjKeyInfo> key_pending_; // attached keyframes
|
std::vector<mjKeyInfo> key_pending_; // attached keyframes
|
||||||
bool deepcopy_; // copy objects when attaching
|
bool deepcopy_; // copy objects when attaching
|
||||||
|
bool attached_ = false; // true if model is attached to a parent model
|
||||||
};
|
};
|
||||||
#endif // MUJOCO_SRC_USER_USER_MODEL_H_
|
#endif // MUJOCO_SRC_USER_USER_MODEL_H_
|
||||||
|
|||||||
@@ -941,6 +941,7 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// attach referencing elements
|
// attach referencing elements
|
||||||
|
other_model->SetAttached(model->deepcopy_);
|
||||||
*model += *other_model;
|
*model += *other_model;
|
||||||
|
|
||||||
// leave the source model in a clean state
|
// leave the source model in a clean state
|
||||||
@@ -1947,6 +1948,7 @@ mjCFrame& mjCFrame::operator+=(const mjCBody& other) {
|
|||||||
last_attached = &body->bodies.back()->spec;
|
last_attached = &body->bodies.back()->spec;
|
||||||
|
|
||||||
// attach referencing elements
|
// attach referencing elements
|
||||||
|
other_model->SetAttached(model->deepcopy_);
|
||||||
*model += *other_model;
|
*model += *other_model;
|
||||||
|
|
||||||
// leave the source model in a clean state
|
// leave the source model in a clean state
|
||||||
|
|||||||
@@ -2544,5 +2544,30 @@ TEST_F(MujocoTest, ApplyNameSpaceToDefaults) {
|
|||||||
mj_deleteVFS(vfs.get());
|
mj_deleteVFS(vfs.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MujocoTest, ErrorWhenCompilingOrphanedSpec) {
|
||||||
|
static constexpr char xml[] = R"(
|
||||||
|
<mujoco>
|
||||||
|
<worldbody>
|
||||||
|
<body name="a"/>
|
||||||
|
</worldbody>
|
||||||
|
</mujoco>
|
||||||
|
)";
|
||||||
|
std::array<char, 1024> er;
|
||||||
|
mjSpec* child = mj_parseXMLString(xml, 0, er.data(), er.size());
|
||||||
|
EXPECT_THAT(child, NotNull()) << er.data();
|
||||||
|
mjSpec* parent = mj_makeSpec();
|
||||||
|
EXPECT_THAT(parent, NotNull());
|
||||||
|
mjsBody* body = mjs_findBody(child, "a");
|
||||||
|
EXPECT_THAT(body, NotNull());
|
||||||
|
mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), nullptr);
|
||||||
|
EXPECT_THAT(frame, NotNull());
|
||||||
|
mjs_attachBody(frame, body, "child-", "");
|
||||||
|
mj_deleteSpec(parent);
|
||||||
|
mjModel* model = mj_compile(child, 0);
|
||||||
|
EXPECT_THAT(model, IsNull());
|
||||||
|
EXPECT_THAT(mjs_getError(child), HasSubstr("by reference to a parent"));
|
||||||
|
mj_deleteSpec(child);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
Reference in New Issue
Block a user