[英]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.