JNA 标记联合映射

JNA tagged union mapping

如何将以下 "tagged union" 结构映射到 JNA?此结构由 libvterm (link to source code) 使用。

/**
 * Tagged union storing either an RGB color or an index into a colour palette
 */
typedef union {
  /**
   * Tag indicating which union member is actually valid. This variable
   * coincides with the `type` member of the `rgb` and the `indexed` struct
   * in memory. */
  uint8_t type;

  struct {
    uint8_t type;
    uint8_t red, green, blue;
  } rgb;

  struct {
    uint8_t type;
    uint8_t idx;
  } indexed;
} VTermColor;

尽管有许多映射都可以工作(任何具有用于获取值的正确方法的 32 位结构),直接映射它的规范方法是使用 JNA 的 Union class.联盟将具有三个要素;您可以定义的 byte 或结构 RGB(Union 的内部 class 可以),或者您将定义的结构 Indexed(同样,一个内部 class)。

Union 将在本机端为最大元素(32 位)分配足够的内存,并且根据结构选项,您可以保证生成的 32 位 C 的前 8 位-side 内存将包含 type 字段;根据该值,您将知道剩余 24 位中包含的内容。

如果您查看 source code of JNA's Variant class which maps the tagged union VARIANT,您会发现它的实现规模稍微复杂一些。 _VARIANT class 包含这五个元素,类似于 union 中的 3 个元素:

        public VARTYPE vt;
        public short wReserved1;
        public short wReserved2;
        public short wReserved3;
        public __VARIANT __variant;

对于联合,__variant 只有一个值有效。在这种情况下,类型设置在这里:

   public void setVarType(short vt) {
        this._variant.vt = new VARTYPE(vt);
   }

更一般地说,您可以查看外部 VARIANT class,它使用 Union class setType() 方法来确定是否存在有效值:它设置对应于在构造函数中设置的活动字段(在本例中为 "_variant")的字符串。 (您也可以使用 class 而不是字符串进行设置。)

在您的情况下,您将希望根据类型值进行初始化,因此您将从 type 作为默认值开始,读取其值,然后切换。

您可以这样定义联合:

public class VTermColor extends Union {
    public class RGB extends Structure {
        public byte type;
        public byte red;
        public byte green;
        public byte blue;
    }

    public class Indexed extends Structure {
        public byte type;
        public byte idx;
    }

    public byte type;
    public RGB rgb;
    public Indexed indexed;

    public VTermColor() {
        // initialize knowing only the type, read its value
        this.setType("type");
        this.read();

        // switch union based on type, re-read
        if ((this.type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB) {
            this.setType("rgb");
        } else {
            this.setType("indexed");
        }
        this.read();
    }

    public VTermColor(Pointer p) {
        super(p);
        // same remaining code as above
    }
}

您可能想创建一些其他 getter 方法来在返回适当的字段之前检查 type 值。

如开头所述,任何 32 位数据结构都可以。一个有点 hacky 的替代方案(牺牲可读性和类型安全性以获得更少的代码)可以简单地始终使用上面定义的 4 字节 RGB 结构。 type 的 getter 总是有效,而 redgreenblue 的 getter 如果有效则有效,否则你可以为 idx 创建一个 getter,它只读取 red.

的值