运行时类型信息(RTTI)

C++中通过<typeinfo><typeindex> 头文件提供了运行时类型信息(RTTI)的支持,主要用于类型识别和类型比较。

1. <typeinfo>

提供运行时类型信息(RTTI),主要包含:

  • typeid 运算符:获取类型的 type_info 对象
  • type_info 类:包含类型信息

示例一:基本类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int i = 42;
double d = 3.14;

std::cout << "i is of type: " << typeid(i).name() << '\n';
std::cout << "d is of type: " << typeid(d).name() << '\n';

if (typeid(i) == typeid(int))
{
std::cout << "i is definitely an int\n";
}
if (typeid(d) == typeid(double))
{
std::cout << "d is definitely a double\n";
}

image-20250605104651798

示例二:多态类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

class Base
{
public:
virtual ~Base()
{
}
};
class Derived : public Base
{
};
int main()
{
Base* b = new Derived();
std::cout << "Actual type of b is: " << typeid(*b).name() << std::endl;
delete b;
return 0;
}

image-20250605105252080

2.<typeindex>

提供std::type_index的类,是type_info的包装类,可以用作关联容器的键

示例三:在map中使用类型索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <unordered_map>
#include <string>

int main()
{
std::unordered_map<std::type_index, std::string> type_names;

type_names[std::type_index(typeid(int))] = "int";
type_names[std::type_index(typeid(double))] = "double";
type_names[std::type_index(typeid(std::string))] = "string";

int x;
double y;
std::string z;
std::cout << "x is a " << type_names[std::type_index(typeid(x))] << '\n';
std::cout << "y is a " << type_names[std::type_index(typeid(y))] << '\n';
std::cout << "z is a " << type_names[std::type_index(typeid(z))] << '\n';
}

image-20250605110538702

Actor设计模式

actor通过消息传递的方式与外界通信。消息传递是异步的。每个actor都有一个邮箱,该邮箱接收并缓存其他actor发过来的消息,actor一次只能同步处理一个消息,处理消息过程中,除了可以接收消息,不能做任何其他操作。
每一个类独立在一个线程里称作Actor,Actor之间通过队列通信,比如Actor1 发消息给Actor2, Actor2 发消息给Actor1都是投递到对方的队列中。好像给对方发邮件,对方从邮箱中取出一样。如下图

96722a9e-8f66-4cb8-a79a-d5aeb900c2f7

Actor模型的另一个好处就是可以消除共享状态,因为它每次只能处理一条消息,所以actor内部可以安全的处理状态,而不用考虑锁机制。

Actor在C++中的简单实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
#ifndef ACTOR_H
#define ACTOR_H

#include <chrono>
#include <condition_variable>
#include <exception>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <stdexcept>
#include <string>
#include <thread>
#include <typeinfo>
#include <unordered_map>
#include <utility>
#include <atomic>

namespace actor
{

class Message
{
public:
virtual ~Message()
{
}
virtual std::string type() const = 0;
virtual std::string toString() const
{
return type();
}
};

template <typename Derived>
class TypedMessage : public Message
{
public:
std::string type() const override
{
return typeid(Derived).name();
}
};

struct TextMessage : public TypedMessage<TextMessage>
{
std::string content;
explicit TextMessage(std::string c) : content(std::move(c))
{
}
std::string toString() const override
{
return "TextMessage: " + content;
}
};

struct StopMessage : public TypedMessage<StopMessage>
{
std::string toString() const override
{
return "StopMessage";
}
};

struct TimeoutMessage : public TypedMessage<TimeoutMessage>
{
std::string toString() const override
{
return "TimeoutMessage";
}
};

using MessagePtr = std::shared_ptr<Message>;

class Actor : public std::enable_shared_from_this<Actor>
{
friend class ActorSystem;

public:
Actor() : running_(true)
{
}

virtual ~Actor()
{
if (thread_.joinable())
thread_.join();
}

void start()
{
thread_ = std::thread(&Actor::run, this);
std::cout << "[" << name_ << "] started." << std::endl;
}

template <typename T, typename... Args>
void send(Args&&... args)
{
auto msg = std::make_shared<T>(std::forward<Args>(args)...);
{
std::lock_guard<std::mutex> lock(mutex_);
mailbox_.push(msg);
}
cv_.notify_all();
}

void send(MessagePtr msg)
{
{
std::lock_guard<std::mutex> lock(mutex_);
mailbox_.push(msg);
}
cv_.notify_all();
}

template <typename T, typename... Args>
void sendAfter(std::chrono::milliseconds delay, Args&&... args)
{
std::weak_ptr<Actor> weakSelf = shared_from_this();
std::thread(
[weakSelf, delay, args...]()
{
std::this_thread::sleep_for(delay);
if (auto self = weakSelf.lock())
{
self->send<T>(args...);
}
})
.detach();
}

void setName(const std::string& name)
{
name_ = name;
}
std::string getName() const
{
return name_;
}

void setProcessTimeout(std::chrono::milliseconds timeout)
{
process_timeout_ = timeout;
}

// 新增 join 接口,外部调用等待线程退出
void join()
{
if (thread_.joinable())
thread_.join();
}

protected:
virtual void onMessage(MessagePtr msg) = 0;
virtual void onTimeout()
{
send<TimeoutMessage>();
}
virtual void onStop()
{
}
virtual void onStart()
{
}

private:
void run()
{
onStart();
while (running_)
{
MessagePtr msg;
{
std::unique_lock<std::mutex> lock(mutex_);
if (cv_.wait_for(lock, process_timeout_, [&]() { return !mailbox_.empty(); }))
{
msg = mailbox_.front();
mailbox_.pop();
}
else
{
onTimeout();
continue;
}
}

std::cout << "[" << name_ << "] received message: " << msg->toString() << std::endl;

if (std::dynamic_pointer_cast<StopMessage>(msg))
{
std::cout << "[" << name_ << "] stopping by StopMessage." << std::endl;
running_ = false;
break;
}

try
{
onMessage(std::move(msg));
}
catch (const std::exception& e)
{
std::cerr << "Exception in " << name_ << ": " << e.what() << std::endl;
}
}
onStop();
std::cout << "[" << name_ << "] stopped." << std::endl;
}

// stop 不 join 线程,只发 StopMessage
void stop()
{
if (running_)
{
send<StopMessage>();
running_ = false; // 确保标志置位,避免死循环
}
}

private:
std::atomic<bool> running_;
std::thread thread_;
std::queue<MessagePtr> mailbox_;
std::mutex mutex_;
std::condition_variable cv_;
std::string name_;
std::chrono::milliseconds process_timeout_{1000};
};

class ActorSystem
{
public:
~ActorSystem()
{
shutdown();
}

template <typename T, typename... Args>
std::shared_ptr<T> createActor(const std::string& name, Args&&... args)
{
auto actor = std::make_shared<T>(std::forward<Args>(args)...);
registerActor(name, actor);
actor->start();
return actor;
}

void registerActor(const std::string& name, std::shared_ptr<Actor> actor)
{
std::lock_guard<std::mutex> lock(mutex_);
if (actors_.count(name))
throw std::runtime_error("Actor already exists: " + name);
actor->setName(name);
actors_[name] = actor;
}

std::shared_ptr<Actor> getActor(const std::string& name)
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = actors_.find(name);
return (it != actors_.end()) ? it->second : nullptr;
}

void broadcast(MessagePtr msg)
{
std::lock_guard<std::mutex> lock(mutex_);
for (auto& [_, actor] : actors_)
actor->send(msg);
}

void shutdown()
{
std::cout << "begin shutdown" << std::endl;

{
std::lock_guard<std::mutex> lock(mutex_);
for (auto& [_, actor] : actors_)
actor->send<StopMessage>();
}

// 等待所有线程退出
{
std::lock_guard<std::mutex> lock(mutex_);
for (auto& [_, actor] : actors_)
actor->join();
actors_.clear();
}

std::cout << "shutdown finished" << std::endl;
}

private:
std::unordered_map<std::string, std::shared_ptr<Actor>> actors_;
std::mutex mutex_;
};

} // namespace actor

#endif // ACTOR_H

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "actor_system.h"
#include <iostream>

using namespace actor;

class Printer : public Actor
{
protected:
void onMessage(MessagePtr msg) override
{
if (auto text = std::dynamic_pointer_cast<TextMessage>(msg))
{
std::cout << "[printer] " << text->content << std::endl;
}
}
};

class Pinger : public Actor
{
public:
Pinger(ActorSystem& system) : system_(system)
{
}

protected:
void onStart() override
{
send<TextMessage>("Ping");
}

void onMessage(MessagePtr msg) override
{
if (auto text = std::dynamic_pointer_cast<TextMessage>(msg))
{
std::cout << "[pinger] received: " << text->content << std::endl;

if (text->content == "Ping")
{
// 通过系统找到名为 "printer" 的 Actor
auto printer = system_.getActor("printer");
if (printer)
{
printer->send<TextMessage>("Ping!");
}
}
}
}

private:
ActorSystem& system_;
};

int main()
{
ActorSystem system;

auto printer = system.createActor<Printer>("printer");
auto pinger = system.createActor<Pinger>("pinger", std::ref(system));

std::this_thread::sleep_for(std::chrono::seconds(1));

system.shutdown();
return 0;
}