2019年3月12日火曜日

Struct でポインタのポインタを扱う方法

概要

前回 複数のポインタを配列を管理する方法を紹介しました
今回は更にそれを Struct で管理する方法を紹介します

環境

  • macOS 10.14.3
  • Ruby 2.5.1p57

サンプルコード

コード全体は以下の通りです

require 'inline'

class MyTest
  inline do |builder|
    builder.add_compile_flags '-x c++', '-std=c++11'
    builder.include '<stdio.h>'
    builder.include '<malloc/malloc.h>'
    builder.prefix '
      typedef struct {
        int count;
        uint8_t *ary[];
      } Data;
    '
    builder.c_singleton '
      VALUE allocate() {
        Data *p = ALLOC(Data);
        return Data_Wrap_Struct(self, NULL, free, p);
      }
    '
    builder.c %q[
      void array_in_array() {
        Data *p;
        Data_Get_Struct(self, Data, p);
        p->count = 3;
        uint8_t *buf1 = (uint8_t *)malloc(16 * sizeof(uint8_t));
        for (unsigned long i = 0; i < malloc_size(buf1); i++) {
          buf1[i] = 'a';
        }
        p->ary[0] = buf1;
        uint8_t *buf2 = (uint8_t *)malloc(16 * sizeof(uint8_t));
        for (unsigned long i = 0; i < malloc_size(buf2); i++) {
          buf2[i] = 'b';
        }
        p->ary[1] = buf2;
        uint8_t *buf3 = (uint8_t *)malloc(16 * sizeof(uint8_t));
        for (unsigned long i = 0; i < malloc_size(buf3); i++) {
          buf3[i] = 'c';
        }
        p->ary[2] = buf3;
      }
    ]
    builder.c %q[
      void show() {
        Data *p;
        Data_Get_Struct(self, Data, p);
        uint8_t **ary = p->ary;
        for (int i = 0; i < p->count; ++i) {
          uint8_t *buf = ary[i];
          for (int j = 0; j < 16; ++j) {
            printf("%c", buf[j]);
          }
          printf("\n");
        }
      }
    ]
  end
end

p = MyTest.new
p.array_in_array
p.show

ポイント解説

Struct の定義で可変長配列 uint8_t *ary[]; を定義しておきます
そして C 側で格納したいポインタを生成して格納する際に Data_Get_Struct で取得したポインタを使って直接配列に格納します

あとは取得する側の関数で uint8_t **ary = p->ary; として取得し多重配列を扱うようにループさせれば OK です

macOS の場合上記でエラーにならないのですが Linux などの環境だと free(): invalid next size (fast) が発生する場合があります
その場合は uint8_t **ary; として Struct 内で配列を参照するポインタを準備すれば OK です
ちなみに macOS でポインタ版にするとぬるぽになります

メモリ空間の扱い方が macOS と Linux だと違うのかもしれません
この辺りは OS を考慮する必要があるので少し面倒になるかもしれません

0 件のコメント:

コメントを投稿