---
# System prepended metadata

title: Tomato Banana's Teaching Room

---

# 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 拜拜