# Tomato Banana's Teaching Room
time: 11/19/21 9:00pm - 11:10pm (- 10min)
```python=
print('hello')
```
1. write 和 fwrite 的区别,我看写binary IO 文件网友用fwrite,那socket 可以用 fwrite吗?socket io 我看是用write多一点,没见过用fwrite的
3. C怎么做serialization?
4. 可以serialize 一个函数吗
5. 当我们serialize一个 C++的类instance的时候,我们写的是什么?肯定跟serialize C struct有区别,因为C struct 只有数据(通常)?
6. Sizeof 好像也有 portable的问题?
7. vector矢量计算是到底是个什么几把东西
动态语言,反射(reflection),serialization,json,codegen,register,protobuf,rpc call, rpc protocol (GRPC)
c++ 反射分静态反射和动态反射。现在有一些静态反射的提案
举个例子:
```
// 用户可以定义一些类型
struct X {
int a;
string b;
};
// 这个时候库作者可能希望有一种机制,能够在编译期获取任意一个类型的一些属性,比如这个类型有哪些 fields,有哪些成员函数,这就属于静态反射的范畴
// 类似这种
for...(auto f : std::experimental::refl<X>::fields ) {
// do something for f
}
现在 c++ 任意写一个类类型之后,库作者没有什么非侵入性的方法读取类里有哪些属性和方法。protobuf 就是用一个 dsl 去生成一堆 c++ 代码,这样就绕过了这个问题。可以看到光标。还没有,有一些提案。
比如自动生成序列化函数
for...(auto f : std::experimental::refl<X>::fields ) {
encode(f);
}
可以啊,你可以对不同的类型定义不同的行为
encode<int> { ... }
encode<string> { ... }
encode<some user-defined class> {
// 静态反射获取属性名和属性类型,然后依次 encode
}
// 有了静态反射,就可以在编译期生成很多特定行为的代码
```
对,python 这种动态语言很容易做动态反射。因为 python 基本上可以认为没有编译期,所以都是动态的。它内存里就有一些 map(function name -> function code) 所以很容易做。但是 c++ 运行期是没有这些信息的。读的话就是比如 linux 的 binary interpreter + cpu (程序指针寄存器 之类的)
JSON.stringify
JSON.parse()
没有关系,JS 是脚本语言,里面的值很容易在各种格式之间互相转换
有点卡,没太听清
可以看到
序列化不一定需要反射参与,但是有了反射会更简单/灵活。
```c++
比如没有反射的话你可以定义一些 json 结构
struct Object {
std::variant<
std::map<string, Object>, // map (str -> Object)
std::vector<Object> // array(Object)
float,
string
> data;
...
}
是的。因为这种是最通用的。嗯,这个库和反射没什么关系
上面这种 key 是存在 map 里的,是运行期的
下面的 key 是编译期确定的
那就用上面那种结构吧,上面这种结构是动态的,没有 schema 的
应该是。没有,我推荐一个 C++ json 库
https://github.com/nlohmann/json
写起来、用起来非常自然(比 miloyip 那个 rapidjson 好用多了)
我的观点是,既然都用 json 了,估计不怎么关心性能,那就以好用为主
rapidjson 总是吹性能,但是不好用
那个 json 库怎么使用吗你是说?很简单的,看 readme 就行
类似这样一个(递归的)结构。
这样你可以定义 encode to json 和 decode from json 函数
但是有了反射之后可能你可以直接让用户定义一些简单的 class 来表达 schema
struct Data {
int x;
std::vector<string> y;
}
然后在编译期生成怎么 encode / decode Data 这个类型。
可能
Data a{1, {"a", "b"}} // c++
可以 encode 成
{"x": 1, "y": ["a", "b"]} // json
这是需要有静态反射的,因为没有的话你很难知道 x 和 y 的 symbol name。 对,这些信息在 c++ 运行期是没有的。
对,c++ 这种编译型语言不需要很多 metadata 就能运行
java 不太一样,java 是编译到 vm 的。java 那个反射是动态反射,反射数据是在运行期取得的。
其实更重要的是用一种语言本身就有的特性规定 schema。就如果 decode 的时候出现了一个名字是 "c" 的 key 就会进入错误处理流程
```
c++?
对,c++类 <--> JSON sh
怎么挂了
https://stackoverflow.com/a/2376224/5983841
这个问题就很显然了,你如果想要把任意一个 c++ 对象序列化,你就要知道这个对象有什么属性,这些属性什么类型。这是需要有反射才能知道的。
```
struct A {
char a[30], b[25], c[15];
int x;
struct B;
string c;
void to_json(){
//this 遍历所有成员变量,然后调用一个 预先约定好的方法,拼成json
// 用 for 需要有反射,你得一个一个属性自己 encode
// 不用 for 就好了
for(auto data_member: ?){ //反射 is needed there
//这里需要的是动态反射是吗?
一般是静态反射,因为效率高一些,可以在编译期展开。你在编译期获取到一个类型的 field,就可以在编译期展开这个循环。运行期就不需要循环了。嗯,就直接展开成 body 了
for(f: ...) do(f) -> do(f1); do(f2); do(f3);
不需要 if else 啊,我在编译期就知道 f 有三个,选择用哪个 do 函数是编译期的。如果有了静态反射,可以做一个动态反射的库。也不一定,java 就只有动态反射,静态反射编译器没暴露出来或者有些情况下做不到。差不多。本质上就是静态反射可以用来生成代码。
auto json_str = B.to_json();
}
str += "\"a\":" + to_json(a) + ",";
str += "\"c\":" + to_json(c) + ",";
... // 可以啊,但是这样太麻烦了,一大堆重复无意义的代码 from_json 会更麻烦,因为需要 parse
讲哪里
}
}
A.to_json() // 这是需要反射的,通过反射去生成一个特定的 to_json 函数
```
加上等号有行号
```python=
a = {"name": "tttt", "age": 17} # 这个比较像我上面写的那个 Object 类似的东西,是完全动态的,没有在运行期限定类型
json.dumps(a)
class X : # sorry
...
b = {"a": X()} # 这样也是可以的,么有限定类型(X() 是 X 类型的临时对象啊)
```
对,c++ 也可以这样
```c++
struct A{
virtual X encode() = 0;
}
就是你可以强制让每个想被序列化的类型都提供 to_json 之类的方法(或者就给所有(基本/非基本)类型都重载 to_json 函数,不用方法)
然后再给基本类型重载 to_json 之类的函数,但是没有反射会特别麻烦
auto to_json(int)
auto to_json(const X&)
或者你就
template <typename T> auto to_json(const T& v) {
return v.to_json()
}
然后重载基本类型的函数
auto to_json(int) {
...
}
auto to_json(float) {
...
}
这样你就可以写成 to_json 方法了
类似的方法, 但是 protobuf 会复杂很多
protobuf 是通过 dsl 生成 c++ 代码的,可以理解为生成很多 to_json / from_json 方法
领域特定语言
就写了一些 schema
message Student {
string id = 1;
string name = 2;
...
}
然后它可以生成一些 struct StudentEncoder 之类的
```
a = {"className": "Foo"}
b = JSON.parse(a)
if(get)
x = json.loads()
if "a" in x:
// do something 这样
这样就很麻烦,没有一种统一的 dispatch 机制
dispatch 就是从一些特定的 key 分发到不同的方法或者类型,可以想象成是 switch 之类的
因为二进制序列化没有一个唯一的很好的解决方案
现在除了 protobuf 还有 avro, flatbuffers, thrift 之类的
c++ 这种追求效率的语言 你如果推出一种官方的方案,可能别人也会去用更高效的方案。python 用户就不太敏感这些。
c++ 也可以啊
```
std::map<string, std::variant<string, float>> a = {
{"name", "tttt"}, {"age", 17}
}
```
然后你需要自己定义一个特定的 encode 函数
没有,c++ 标准库没有相关 json 的东西
c++ 用类定义这种 schema 是编译期的 schema。
js python 也可以有 schema,但是是运行期的一些 check,比如自己写一些方法去 check 返回出来的东西的格式(一些 if 之类的)
指针的话,有几种方法吧感觉。一种就是直接序列化指向的对象(to_json(*x));或者就是给一个 label(或者称为 symbol),然后最后给一个 symbol list。
是,不过一般这些协议不会考虑指针这种问题吧,这种问题太特定语言了。
JSON is a schema-less protocol.
嗯 protobuf 之所以需要 schema 是因为它序列化之后的 data 里面没有很多信息,然后这个 schema 本身就是元信息。比如 key name 在序列化后的 data 里面是没有。你需要通过 schema 来恢复这些信息
```
struct StarBucks{
static const char className[20] = "StartBucks001";
}
if(className == "StartBucks001"){
}elif (xxx){
}else{
}
```
char[20] +
前 20 个字节是 class name 吗,后面是这个类型的对象的二进制吗(其实不需要长度,因为 class name 就确定了长度,你可以有一个 class name 到 sizeof 的映射)
但是这样做有非常多的问题。首先,你的 struct 需要跨架构吗。不是,protobuf 最大的意义在于 schema 的版本变更管理,比如你在业务里需要在 schema 里删除或者加入一些新字段,这时候你如果直接存二进制的话,原来的那些客户端就不能用了(假设你更新了服务器代码的 struct)。你不能强制要求更新客户端。还有安全问题。不能跨架构、跨编译器。还有就是如果 struct 里有指针,这个序列化的结果就没有意义了。
```
struct A {
char *a, *b, *c;
int d;
}
write(fh, ptr, sizeof(struct a)).
read(fh, ptr, sizeof(struct a))
```
你 read 和 write 并不一定在一台计算机上啊。序列化的意义就在于通信。可以,但是意义不大。你怎么存动态数组呢,怎么存 map 呢。对,那就很返璞归真了,这个协议就没啥意义了。你自己写 encode 动态数组还是要关心数据的格式。
1. write 和 fwrite 的区别,我看写binary IO 文件网友用fwrite,那socket 可以用 fwrite吗?socket io 我看是用write多一点,没见过用fwrite的 x
fwrite 本身就是针对文件的吧(unix 里一切皆文件)
fwrite 有 buffer 吗
3. C怎么做serialization? x
4. 可以serialize 一个函数吗 x
5. 当我们serialize一个 C++的类instance的时候,我们写的是什么?肯定跟serialize C struct有区别,因为C struct 只有数据(通常)?x
没什么区别感觉
6. Sizeof 好像也有 portable的问题? x
7. vector矢量计算是到底是个什么几把东西
函数序列化就相当于做一个脚本语言了。你把脚本语言代码传过去,然后在那边解释执行。
就是你两边要有同一个解释器。然后代码互相传,用同一个解释器解释执行。
所以动态语言,脚本语言,是不是可以理解为有一个living thing在那里的,类似一个,活着的编译器?
```
struct StarBucks{
static const char className[20] = "StartBucks001";
char b[100] customer;
void doTransation(){
this.sendMoneyTo(this.customer);
}
}
```
protocol:
char[20] + "doTransation" + id
```
map<id, StarBucks>
```
这是不是就是一种RPC call
restful就是语义化get, post ,put ,delete的http 的意思呗
是非常简陋、原始的 rpc
restful 也可以理解为一种 rpc
嗯
grpc 首先是用 protobuf 做为协议
rpc 对比一般的程序里的 call,不同的地方就是要通过网络,所以要考虑的事情会多很多:这次请求会不会失败?失败了要不要再发一遍?还有就是 grpc 有 streaming 功能,可以持续的发送一串数据,在发送的同时那边就可以进行处理(不必等待都发完,是交互式的)肯定能做到类似的事情,但是不高效/写起来很复杂。
动态反射就是运行期获取一些 metadata,或者是获取某个对象的实际的类型而非声明的类型。算是。
python debug的时候,可以写type(a)以及dir(a)
gpu? 矢量计算我觉得就是指的比如矩阵的计算吧
不一定要用 gpu,也可以用 cpu,只是 gpu 会算的快一些
gpu 可以同时并行几百个线程
ok
hao 拜拜