ExMujoco.Gen.Header (ExMujoco v0.1.1)

Copy Markdown View Source

Reads MuJoCo's C headers into data.

MuJoCo generates its own Python bindings the same way — python/mujoco/introspect parses the headers and checks the result in. This is the same idea in Elixir: the headers are the source of truth, the catalogue is checked in beside us, and mix ex_mujoco.gen --check proves the two agree.

Three things are read:

  • functions — every MJAPI declaration in mujoco.h and the headers it includes;
  • structs — the members of mjModel and mjData, with the dimensions of their arrays taken from mjxmacro.h, which exists to be read like this;
  • enums — every typedef enum mjtX { … } mjtX;.

Nothing here decides what is bound; it only says what exists. ExMujoco.Gen holds that decision, in c_src/manifest.exs.

Summary

Types

A C type as it appears in a declaration: mjtNum*, const char*.

Functions

What a member is, as far as a binding goes.

What an array of this element type is, as {tag, cpp_type}.

Every typedef enum mjtX { … } mjtX; in one header.

The MJAPI declarations in one header.

Everything the headers say, as one map.

The members of one of MuJoCo's structs, with array dimensions from mjxmacro.h.

Why a member cannot be read as an array, when it cannot.

Types

ctype()

@type ctype() :: %{name: String.t(), const: boolean(), pointers: non_neg_integer()}

A C type as it appears in a declaration: mjtNum*, const char*.

enum()

@type enum() :: %{
  name: String.t(),
  items: [%{name: String.t(), value: integer() | nil, doc: String.t()}]
}

function_decl()

@type function_decl() :: %{
  name: String.t(),
  returns: ctype(),
  parameters: [parameter()],
  doc: String.t(),
  variadic: boolean()
}

member()

@type member() :: %{
  name: String.t(),
  type: ctype(),
  array: String.t() | nil,
  doc: String.t()
}

parameter()

@type parameter() :: %{
  name: String.t(),
  type: ctype(),
  array: String.t() | nil,
  kind: :integer | :float | :string | :buffer | :resource | :opaque | :callback
}

Functions

classify_member(member, dims)

What a member is, as far as a binding goes.

  • :integer and :float — one value of the struct, returned as is;
  • :array — a binary of elements plus its two extents;
  • :unsupported — nothing a caller can name, with the reason why.

element_type(c_type)

What an array of this element type is, as {tag, cpp_type}.

enums_in(path)

@spec enums_in(Path.t()) :: [enum()]

Every typedef enum mjtX { … } mjtX; in one header.

functions_in(path)

@spec functions_in(Path.t()) :: [function_decl()]

The MJAPI declarations in one header.

read(dir)

@spec read(Path.t()) :: %{
  functions: [function_decl()],
  structs: %{required(String.t()) => [member()]},
  enums: [enum()],
  version: non_neg_integer() | nil
}

Everything the headers say, as one map.

dir is a MuJoCo include root, the one MUJOCO_DIR names: it must hold mujoco/mujoco.h.

struct_members!(dir, struct)

@spec struct_members!(Path.t(), String.t()) :: [member()]

The members of one of MuJoCo's structs, with array dimensions from mjxmacro.h.

Value members (mjtSize nq) and pointers (mjtNum* geom_pos) are both here, because a caller reads both. Arrays declared inline (mjtNum solver_fwdinv[2]) carry their dimension in the declaration; a pointer carries it in the X-macro lists, which is where MuJoCo itself gets it for mjvScene and the bindings.

unexposed_reason(struct, member)

@spec unexposed_reason(String.t(), String.t()) :: String.t() | nil

Why a member cannot be read as an array, when it cannot.