11

私は64ビットプラットフォームでこのC構造体を使用しており、の和集合のui32vフィールドにアクセスしようとしています。

struct _GNetSnmpVarBind {
  guint32       *oid;       /* name of the variable */
  gsize     oid_len;    /* length of the name */
  GNetSnmpVarBindType   type;       /* variable type / exception */
  union {
    gint32   i32;           /* 32 bit signed   */
    guint32  ui32;          /* 32 bit unsigned */
    gint64   i64;           /* 64 bit signed   */
    guint64  ui64;          /* 64 bit unsigned */
    guint8  *ui8v;          /*  8 bit unsigned vector */
    guint32 *ui32v;         /* 32 bit unsigned vector */
  }         value;      /* value of the variable */
  gsize     value_len;  /* length of a vector in bytes */
};

ユニオン要素ごとにCラッパー関数を作成することもできますが、教訓的な目的のために、Goで作業したいと思います。ui32vフィールドにアクセスしようとしている方法は次のとおりです。

func union_to_guint32_ptr(cbytes [8]byte) (result *_Ctype_guint32) {
  buf := bytes.NewBuffer(cbytes[:])
  var ptr uint64
  if err := binary.Read(buf, binary.LittleEndian, &ptr); err == nil {
    return (*_Ctype_guint32)(unsafe.Pointer(ptr))
  }
  return nil
}

ただし、これにより、ptr(タイプuint64)をタイプunsafe.Pointerに変換できないというエラーが発生します。

では、 uint64をC guint32を指すGoタイプに変換するにはどうすればよいですか?uintptrにキャストしてから、* _ Ctype_guint32にキャストし、uintptrにキャストしてから、unsafe.Pointer、..を使用するというさまざまな組み合わせを試しました。

私の理由は次のとおりです。8バイトの配列が渡されました。それをuint64に変換します。これがメモリアドレスです。それをguint32(つまり、guint32のC配列)へのポインターにキャストし、結果としてそれを返します。これは、guint32*としての共用体フィールド「value」です。


コンテクスト

後で、すでに機能していることがわかっている関数を使用して、guint32のC配列をvalue_lenフィールドを使用する文字列に変換したいと思います。

guint32_star := union_to_guint32_ptr(data.value)
result += OidArrayToString(guint32_star, data.value_len)

Cコードはgsnmpからのものです。

4

4 に答える 4

5

解決策は、最初に uintptr にキャストし、次にunsafe.Pointerにキャストすることでした。つまり、2 つの別々のキャストです。

func union_to_guint32_ptr(cbytes [8]byte) (result *_Ctype_guint32) {
    buf := bytes.NewBuffer(cbytes[:])
    var ptr uint64
    if err := binary.Read(buf, binary.LittleEndian, &ptr); err == nil {
        uptr := uintptr(ptr)
        return (*_Ctype_guint32)(unsafe.Pointer(uptr))
    }   
    return nil 
}                

コマンド ライン ツールで結果を比較してこれを確認したところ、正しい結果が返されました。


環境

// gsnmp._Ctype_gpointer -> *gsnmp._Ctype_GNetSnmpVarBind
data := (*C.GNetSnmpVarBind)(out.data)

switch VarBindType(data._type) {
case GNET_SNMP_VARBIND_TYPE_OBJECTID:
    result += "GNET_SNMP_VARBIND_TYPE_OBJECTID" + ":"
    guint32_star := union_to_guint32_ptr(data.value)
    result += OidArrayToString(guint32_star, data.value_len)
于 2013-01-29T13:24:42.303 に答える
3

ソニアはすでに彼女自身の質問に答えています.2つの型変換が必要な理由を提供したいだけです.

unsafe.Pointerのドキュメントから:

1) 任意の型のポインター値を Pointer に変換できます。

2) Pointer は、任意の型のポインター値に変換できます。

3) uintptr は Pointer に変換できます。

4) Pointer は uintptr に変換できます。

はポインターではないためvar ptr uint64(型uint64はポインターではないため)、ルール 1ptrを使用して に直接変換することはできません。したがって、最初に に変換し、次に から次のルール 3に変換する必要があります。unsafe.PointerptruintptruintptrPointer

于 2015-08-08T23:32:58.750 に答える
1

CGOドキュメントから:

構造体、共用体、または列挙型に直接アクセスするには、C.struct_stat のように、struct_、union_、または enum_ を前に付けます。

だから私(テストされていない)コードは次のようなものかもしれないと思います:

myUint32var := somePtrTo_GNetSnmpVarBind.union_guint32

が指す構造体の共用体の guint32 メンバーにアクセスするためsomePtrTo_GNetSnmpVarBind

于 2013-01-29T11:06:41.970 に答える