Internals

Autogenerated wrappers

The Cython struct wrappers are generated by scripts/gen_wrappers.py. To illustrate how the wrappers work, let’s consider a toy C structure and the corresponding generated Cython cdef class. Here’s a stripped down version of mjData with a scalar member and a pointer (array) member:

typedef struct _mjData {
    int ne;
    mjtNum* qpos;  // (nq x 1)
} mjData;

Here’s the corresponding generated Cython wrapper code:

cdef class PyMjData(object):
    cdef mjData* ptr
    cdef mjModel* _model
    cdef np.ndarray _qpos

    cdef void _set(self, mjData* p, mjModel* model):
        self.ptr = p
        self._model = model
        self._qpos = _wrap_mjtNum_1d(p.qpos, model.nq)

    @property
    def ne(self): return self.ptr.ne

    @ne.setter
    def ne(self, int x): self.ptr.ne = x

    @property
    def qpos(self): return self._qpos

cdef PyMjData WrapMjData(mjData* p, mjModel* model):
    cdef PyMjData o = PyMjData()
    o._set(p, model)
    return o

PyMjData is the wrapper class for exposing the underlying Mujoco structure to Python; it doesn’t perform any memory mangement. A user writing Cython code can create this wrapper using WrapMjData. A mjModel pointer must be passed because the shape of a mjData member, namely qpos, depends on model->nq.

Each field of mjData corresponds to some generated piece of code in PyMjData that depends on the type of that field. For example, ne is a scalar integer, so it gets exposed as a pair of getter and setter methods in PyMjData. qpos is an array represented as a pointer to its first element, so it’s wrapped with a NumPy array by _wrap_mjtNum_1d and is exposed with a getter for that NumPy array.

The function _wrap_mjtNum_1d creates a Cython memoryview from the data pointer and converts it to a NumPy array pointing to the same memory:

cdef inline np.ndarray _wrap_mjtNum_1d(mjtNum* a, int shape0):
    if shape0 == 0: return None
    cdef mjtNum[:] b = <mjtNum[:shape0]> a
    return np.asarray(b)

Similar functions for other types are also generated as required.

Keep in mind that the only reason to use these autogenerated wrappers is to allow Python users of the Cython code to easily access Mujoco data (for instance the MjSim Cython class, found in ``cymj/cymj.pyx`). If you’re writing Cython code and you don’t need the user to access Mujoco data from Python, then there is no reason to use these wrappers.