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