| RFC-0022:澄清:结构体成员的默认值 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | FIDL 结构体成员可以具有默认值。目前,默认值的支持已部分实现(请参阅下文),此提案旨在明确默认值应如何运作。 |
| 作者 | |
| 提交日期(年-月-日) | 2018-07-17 |
| 审核日期(年-月-日) | 2018-09-27 |
摘要
FIDL 结构体成员可以具有默认值。目前,默认值的支持已部分实现(请参阅下文),此提案旨在明确默认值应如何运作。
设计初衷
- 它在语言绑定中提供规律性,提供针对不一致或意外使用的保护,并且
- 当语言要求显式初始化时,可避免繁琐的手动逐个成员初始化,并
非激励性内容包括:
- 以有线格式保存字节
它不是为了节省有线格式的字节数,也不是为了节省编码或解码的处理能力。
今天的实现方式
可以在 FIDL 语言中表达结构体成员的默认值:
- (+) 支持数值字面量、布尔值字面量和字符串字面量。
- (-)
fidlc不提供字面量对结构体成员的可分配性类型检查。 可以将字符串字面量“hello”分配给布尔值,将负数分配给无符号整数,或将超出范围的数字分配给 int16。 - (-) 语言绑定支持不一致,目前仅支持 C++ 和 Dart 绑定。 不支持 Go、C 或 Rust。
例如(来自 //zircon/system/host/fidl/examples/types.test.fidl):
struct default_values {
bool b1 = true;
bool b2 = false;
int8 i8 = -23;
int16 i16 = 34;
int32 i32 = -34595;
int64 i64 = 3948038;
uint8 u8 = 0;
uint16 u16 = 348;
uint32 u32 = 9038;
uint64 u64 = 19835;
float32 f32 = 1.30;
float64 f64 = 0.0000054;
string s = "hello";
};
设计
可以在结构体成员上定义默认值。默认值显示在字段定义的末尾,采用类似于 C 语言的 = {value} 模式。
语法
// cat.fidl
enum CatAction : int8 {
SIT = -10;
WALK = 0;
SNEAK = 2;
};
struct Location {
uint8 pos_x = 10; // Position X
uint8 pos_y; // Position Y. Default unspecified. Fall-back to 0
float32 pos_z = 3.14; // Position Z.
float32 pos_t; // Default unspecified. Fall-back to 0.0
};
struct Cat {
string name; // Automatic default to empty string
CatAction action = CatAction::SNEAK;
Location loc;
};
语义
请参阅 RFC-006,其中明确了默认值的语义以及对绑定的要求。
支持的类型
- 原始类型:
bool、int8、int16、int32、int64、uint8、uint16、uint32、uint64、float32、float64
- 不可为 null 的
string、string:Nstring:N应将预留但未使用的内存清零。
不支持的类型
array<T>:N- 设为零
- 不可为 null 的类型:
vector<T>、vector<T>:N- 设为零
- 可为 null 的类型:
string?、string:N?、vector<T>?、vector<T>:N?- 设为 null
handlestruct- 虽然
struct中的每个单独成员可能都有默认值,但struct本身没有默认值。
- 虽然
union- 为避免任何冲突,
union的成员或union的子结构(任意深度)的任何默认值都将被忽略。
- 为避免任何冲突,
默认设置的细微差别
重点在于值本身,而不是分配值的方式。 这至少意味着两件事:
- 没有区别 - 无论是由于相关参数被其他机制明确分配,还是由于其他原因而使用默认值,结果都是一样的。
- 在序列化或反序列化时,没有额外的(透明)逻辑层来分配值。
实现
以下是 C、Rust 和 Go 绑定的实现思路示例
// in FIDL "default.fidl"
struct Location {
uint8 pos_x = 10;
uint8 pos_y = 20;
uint8 pos_x; // Should be set to "zero" according to above.
};
// C binding "defaults/fidl.h"
typedef struct _Location_raw {
uint8_t pos_x;
uint8_t pos_y;
uint8_t pos_z
} Location;
Location Location_default = { 10, 20, 0 }; // Or in the source file.
// May be used for memcmp,
memcpy, etc.
#define Location(my_instance) Location my_instance = Location_default;
// C code "example.c"
#include <fidl.h>
void showme(Location loc) {
printf("(%u, %u, %u)\n", loc.pos_x, loc.pos_y, loc.pos_z);
}
int main() {
Location(alpha);
Location beta;
Location gamma = Location_default;
showme(alpha); showme(beta); showme(gamma);
return 0;
}
// Rust binding
struct Location {
pos_x: u8,
pos_y: u8,
pos_z: u8,
}
impl std::default::Default for Location {
fn default() -> Self { Self { pos_x: 10, pos_y: 20, pos_z: 0 } }
}
// Go binding, using export control
type location struct {
pos_x uint8
pos_y uint8
pos_z uint8
}
Func NewLocation() location {
loc := location{}
loc.pos_x = 10
loc.pos_y = 20
// loc.pos_z = 0 Maybe omitted.
return loc
}
性能
不适用
安全
不适用
测试
不适用
向后兼容性
此变更会导致 FIDL 文件源向后不兼容。无需更改 ABI 或线格式。
缺点、替代方案和未知因素
尚未评估在所有语言绑定中实现此功能是否简单明了。
在先技术和参考资料
协议缓冲区、Flat 缓冲区提供默认值。Golang 有一个零值的概念,即声明的变量如果没有明确的初始值,则会明确初始化为零。
开源方法:
// From https://github.com/creasty/defaults
type Sample struct {
Name string `default:"John Smith"`
Age int `default:"27"`
Gender Gender `default:"m"`
Slice []string `default:"[]"`
SliceByJSON []int `default:"[1, 2, 3]"` // Supports JSON format
Map map[string]int `default:"{}"`
MapByJSON map[string]int `default:"{\"foo\": 123}"`
Struct OtherStruct `default:"{}"`
StructPtr *OtherStruct `default:"{\"Foo\": 123}"`
NoTag OtherStruct // Recurses into a nested struct even without a tag
OptOut OtherStruct `default:"-"` // Opt-out
}