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``,
|
||||
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
|
||||
parent will be compiled using Y.
|
||||
interperted. Compiler flags are carried over during attachment, so the child model will be compiled using the child
|
||||
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:
|
||||
|
||||
|
||||
@@ -3972,6 +3972,9 @@ mjModel* mjCModel::Compile(const mjVFS* vfs, mjModel** m) {
|
||||
warningtext[0] = 0;
|
||||
|
||||
try {
|
||||
if (attached_) {
|
||||
throw mjCError(0, "cannot compile child spec if attached by reference to a parent spec");
|
||||
}
|
||||
if (setjmp(error_jmp_buf) != 0) {
|
||||
// TryCompile resulted in an mju_error which was converted to a longjmp.
|
||||
std::string error_msg = errortext;
|
||||
|
||||
@@ -317,6 +317,9 @@ class mjCModel : public mjCModel_, private mjSpec {
|
||||
// set deepcopy flag
|
||||
void SetDeepCopy(bool deepcopy) { deepcopy_ = deepcopy; }
|
||||
|
||||
// set attached flag
|
||||
void SetAttached(bool deepcopy) { attached_ |= !deepcopy; }
|
||||
|
||||
private:
|
||||
// settings for each defaults class
|
||||
std::vector<mjCDef*> defaults_;
|
||||
@@ -428,5 +431,6 @@ class mjCModel : public mjCModel_, private mjSpec {
|
||||
mjCError errInfo; // last error info
|
||||
std::vector<mjKeyInfo> key_pending_; // attached keyframes
|
||||
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_
|
||||
|
||||
@@ -941,6 +941,7 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
|
||||
}
|
||||
|
||||
// attach referencing elements
|
||||
other_model->SetAttached(model->deepcopy_);
|
||||
*model += *other_model;
|
||||
|
||||
// leave the source model in a clean state
|
||||
@@ -1947,6 +1948,7 @@ mjCFrame& mjCFrame::operator+=(const mjCBody& other) {
|
||||
last_attached = &body->bodies.back()->spec;
|
||||
|
||||
// attach referencing elements
|
||||
other_model->SetAttached(model->deepcopy_);
|
||||
*model += *other_model;
|
||||
|
||||
// leave the source model in a clean state
|
||||
|
||||
@@ -2544,5 +2544,30 @@ TEST_F(MujocoTest, ApplyNameSpaceToDefaults) {
|
||||
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 mujoco
|
||||
|
||||
Reference in New Issue
Block a user