繁体   English   中英

如何将 Go 绑定建模到使用联合的 C 结构?

[英]How to model Go bindings to C structs that use unions?

我目前正在为libfreefare编写Go 包装器 libfreefare 的 API 包含以下函数:

struct mifare_desfire_file_settings {
    uint8_t file_type;
    uint8_t communication_settings;
    uint16_t access_rights;
    union {
    struct {
        uint32_t file_size;
    } standard_file;
    struct {
        int32_t lower_limit;
        int32_t upper_limit;
        int32_t limited_credit_value;
        uint8_t limited_credit_enabled;
    } value_file;
    struct {
        uint32_t record_size;
        uint32_t max_number_of_records;
        uint32_t current_number_of_records;
    } linear_record_file;
    } settings;
};

int      mifare_desfire_get_file_settings (MifareTag tag, uint8_t file_no, struct mifare_desfire_file_settings *settings);

包装这样一个功能的理想解决方案是什么? 如果struct mifare_desfire_file_settings不包含任何联合,我的包装器可能看起来像这样:

type DESFireFileSettings struct {
    // all fields exported, no methods
}

func (t DESFireTag) FileSettings(fileNo byte) (DESFireFileSettings, error)

我应该如何进行?

您需要考虑如何更新联合中的字段。 显然,如果没有验证,你不能让用户这样做。 他们可以做不一致的更新。 考虑做这样的事情:

package mifare

const (
    MDFTStandarDataFile            = 0x00
    MDFTBackupDataFile             = 0x01
    MDFTValueFileWithBackup        = 0x02
    MDFTLinearRecordFileWithBackup = 0x03
    MDFTCyclicRecordFileWithBackup = 0x04
)

type StandardFile struct {
    FileSize uint32
}

type ValueFile struct {
    LowerLimit           int32
    UpperLimit           int32
    LimitedCreditValue   int32
    LimitedCreditEnabled uint8
}

type LinearRecordFile struct {
    Record_size            uint32
    MaxNumberOfRecords     uint32
    CurrentNumberOfRecords uint32
}

type DESFireFileSettings struct {
    FileType              uint8
    CommunicationSettings uint8
    AccessRights          uint16
    settings              struct {
        StandardFile
        ValueFile
        LinearRecordFile
    }
}

func (fs *DESFireFileSettings) StandardFile() (StandardFile, error) {
    // if not valid for FileType, return error
    return fs.settings.StandardFile, nil
}

func (fs *DESFireFileSettings) SetStandardFile(standardFile StandardFile) error {
    // if not valid for FileType, return error
    fs.settings.StandardFile = standardFile
    return nil
}

func (fs *DESFireFileSettings) ValueFile() (ValueFile, error) {
    // if not valid for FileType, return error
    return fs.settings.ValueFile, nil
}

func (fs *DESFireFileSettings) SetValueFile(valueFile ValueFile) error {
    // if not valid for FileType, return error
    fs.settings.ValueFile = valueFile
    return nil
}

func (fs *DESFireFileSettings) LinearRecordFile() (LinearRecordFile, error) {
    // if not valid for FileType, return error
    return fs.settings.LinearRecordFile, nil
}

func (fs *DESFireFileSettings) SetLinearRecordFile(linearRecordFile LinearRecordFile) error {
    // if not valid for FileType, return error
    fs.settings.LinearRecordFile = linearRecordFile
    return nil
}

我部分同意peterSO,但我对DESFireFileSettings结构采取了不同的方法,因为这是一个包装器而不是端口。 (如果这是一个端口,我会使用接口)。

考虑一下,类似于他所写的:

type StandardFile struct {
    FileSize uint32

    _ [9]byte // for padding to size of ValueFile
}

type ValueFile struct {
    LowerLimit           int32
    UpperLimit           int32
    LimitedCreditValue   int32
    LimitedCreditEnabled uint8
}

type LinearRecordFile struct {
    Record_size            uint32
    MaxNumberOfRecords     uint32
    CurrentNumberOfRecords uint32

    _ [1]byte // for padding to size of ValueFile
}

但是,对于DESFireFileSettings

type DESFireFileSettings struct {
    fs C.mifare_desfire_get_file_settings
}

请注意,在Go中,联合会被记录为[n]个字节,其中n是联合的最大大小。 所以在这种情况下,n是13(三个4字节int32和一个1字节uint8)。 使用不安全:

func (dfs *DESFireFileSettings) SetStandardFile(standardFile StandardFile) error {
     // If not valid, return an error
     dfs.fs.settings = *((*[13]byte)(unsafe.Pointer(&standardFile)))
     return nil
}

如果您想避免不安全,可以使用C函数使其更明确

void setStandardFile(*mifare_desfire_file_settings fs, uint32_t size) {
    fs.settings.standardfile.file_size = size
}

在Go中写一个passthrough函数:

func (dfs *DESFireFileSettings) SetStandardFile(standardFile StandardFile) error {
      // If not valid, return an error
      C.setStandardFile(&dfs.fs, C.uint32_t(standardFile.FileSize))
 }

我个人而言,更喜欢在这种情况下直接设置它是unsafe ,但两种方式都是有效的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM