好吧,又一种C++事件回调封装以及相关的零碎讨论
2012-06-16 13:35
387 查看
好吧,又一种C++事件回调封装以及相关的零碎讨论
分类:C C++ 程序设计
2011-10-19 22:30
23人阅读 评论(0)
收藏
举报
好吧,又一种C++事件回调封装以及相关的零碎讨论
事件回调机制的实现可能是C++领域里最大众化的代码游戏之一。一方面,C++并没有这个机制的语法层支持,这导致了众多商业和开源框架各自实现了风格迥异的事件回调。尤其是GUI方面,MFC提供了一层薄薄的消息映射;ATL用了一个thunk技术(不熟悉的可以google一下),简单的说就是偷偷的把this放到栈上;VCL够凶悍,直接扩充了编译器,提供了一个__closure关键字各种成员函数的指针通吃;QT的signal/slot很俏丽,也够强大……
另一方面,如果做一个调查,当一个C++使用者比较熟悉C++的一些特性,亲手写过一些程序之后,想亲手封装一些东西,那么他会封装什么?我想stream IO包括socket)、配置文件、日志、内存池、线程、简单的容器以及本次说的事件回调绝对是高频选项。
随便google或者百度一下,C++爱好者实现的事件回调或者相关论述多到十倍于足以证明我刚才的第二个看法的程度。
比如这里,这里,这里,以及这里
……
如果这些还没让你厌倦,你可以尝试看看下面这个:
1
struct BaseObject
2
{
3
virtual ~BaseObject()
{}
4
};
1
struct ISupportReceiveDestroyMessage : public virtual Interface
2
{
3
virtual ~ISupportReceiveDestroyMessage()
{}
4
virtual void receiveDestroyMessage(BaseObject *pNotifier) = 0;
5
};
6
struct ISupportRelationship : public virtual Interface
{
virtual ~ISupportRelationship()
{}
virtual void regRelationship(ISupportReceiveDestroyMessage *pRelate) = 0;
virtual void unregRelationship(ISupportReceiveDestroyMessage *pRelate) = 0;
}
1
class CanUseTrigger : public BaseObject,
2
public virtual ISupportReceiveDestroyMessage,
3
public virtual ISupportRelationship
4
{
5
public:
6
virtual ~CanUseTrigger()
7
{
8
for(std::vector<ISupportReceiveDestroyMessage *>::iterator it = m_vRelateList.begin();
9
it != m_vRelateList.end();
10
it++)
11
{
12
(*it)->receiveDestroyMessage(this);
13
}
14
}
15
16
virtual void regRelationship(ISupportReceiveDestroyMessage *pRelate)
17
{
18
std::vector<ISupportReceiveDestroyMessage *>::iterator it =
19
std::find(m_vRelateList.begin(), m_vRelateList.end(), pRelate);
20
21
if (it == m_vRelateList.end())
22
{
23
m_vRelateList.push_back(pRelate);
24
}
25
}
26
27
virtual void unregRelationship(ISupportReceiveDestroyMessage *pRelate)
28
{
29
m_vRelateList.erase(std::remove(m_vRelateList.begin(), m_vRelateList.end(), pRelate),
30
m_vRelateList.end());
31
}
32
33
virtual void receiveDestroyMessage(BaseObject *pNotifier)
34
{
35
ISupportReceiveDestroyMessage *pTmp = dynamic_cast<ISupportReceiveDestroyMessage *>(pNotifier);
36
if (pTmp)
37
{
38
unregRelationship(pTmp);
39
}
40
}
41
42
private:
43
std::vector<ISupportReceiveDestroyMessage *> m_vRelateList;
44
};
45
46
class TriggerBase : public BaseObject,
47
public virtual ISupportReceiveDestroyMessage
48
{
49
public:
50
virtual ~TriggerBase()
51
{
52
for (std::vector<Relationship>::iterator it = m_vRelationships.begin();
53
it != m_vRelationships.end();
54
it++)
55
{
56
it->pOther->receiveDestroyMessage(this);
57
}
58
}
59
60
virtual void receiveDestroyMessage(BaseObject *pNotifier)
61
{
62
CanUseTrigger *pTmp = dynamic_cast<CanUseTrigger *>(pNotifier);
63
if (!pTmp)
64
{
65
return;
66
}
67
68
m_vRelationships.erase(std::remove(m_vRelationships.begin(), m_vRelationships.end(), Relationship(pTmp)),
69
m_vRelationships.end());
70
}
71
72
protected:
73
void regToUser(CanUseTrigger *pUser)
74
{
75
std::vector<Relationship>::iterator itUser
76
= std::find(m_vRelationships.begin(), m_vRelationships.end(), Relationship(pUser));
77
78
if (m_vRelationships.end() != itUser)
79
{
80
++(itUser->RefCount);
81
}
82
else
83
{
84
m_vRelationships.push_back(Relationship(pUser));
85
m_vRelationships[m_vRelationships.size() - 1].RefCount++;
86
pUser->regRelationship(this);
87
}
88
}
89
90
void unregFormUser(CanUseTrigger *pUser)
91
{
92
std::vector<Relationship>::iterator itUser
93
= std::find(m_vRelationships.begin(), m_vRelationships.end(), Relationship(pUser));
94
95
if (m_vRelationships.end() != itUser)
96
{
97
--(itUser->RefCount);
98
if(itUser->RefCount < 1)
99
{
100
m_vRelationships.erase(itUser);
101
pUser->unregRelationship(this);
102
}
103
}
104
}
105
106
struct Relationship
107
{
108
CanUseTrigger *pOther;
109
i32_t RefCount;
110
Relationship(CanUseTrigger *pUser) : pOther(pUser), RefCount(0)
{}
111
112
Relationship &operator=(const Relationship &rhs)
113
{
114
pOther = rhs.pOther;
115
RefCount = rhs.RefCount;
116
117
return *this;
118
}
119
bool operator==(const Relationship &rhs)
120
{
121
return pOther == rhs.pOther;
122
}
123
bool operator==(const CanUseTrigger *rhs)
124
{
125
return pOther == rhs;
126
}
127
};
128
129
template <class P>
130
struct Channel1Base
131
{
132
typedef struct
{} is_member_t;
133
typedef struct
{} is_not_member_t;
134
135
virtual ~Channel1Base()
{}
136
virtual void invoke(P p) = 0;
137
virtual bool equal(Channel1Base<P> *pOther) = 0;
138
virtual bool isOwner(CanUseTrigger *pCandidate) = 0;
139
};
140
141
template <class P1, class P2>
142
struct Channel2Base
143
{
144
typedef struct
{} is_member_t;
145
typedef struct
{} is_not_member_t;
146
147
virtual ~Channel2Base()
{}
148
virtual void invoke(P1 p1, P2 p2) = 0;
149
virtual bool equal(Channel2Base<P1, P2> *pOther) = 0;
150
virtual bool isOwner(CanUseTrigger *pCandidate) = 0;
151
};
152
153
template <class P>
154
struct NakedChannel1 : public Channel1Base<P>
155
{
156
typedef Channel1Base<P>::is_not_member_t member_spec_t;
157
typedef void (* method_t)(P);
158
159
NakedChannel1(method_t pMethod) : m_pMethod(pMethod)
{}
160
~NakedChannel1()
{}
161
virtual void invoke(P p)
162
{
163
if (m_pMethod)
164
{
165
m_pMethod(p);
166
}
167
}
168
169
virtual bool equal(Channel1Base<P> *pOther)
170
{
171
NakedChannel1<P> *pTmp = dynamic_cast<NakedChannel1<P> *>(pOther);
172
if (!pTmp)
173
{
174
return false;
175
}
176
177
return m_pMethod == pTmp->m_pMethod;
178
}
179
180
virtual bool isOwner(CanUseTrigger *pCandidate)
181
{
182
return false;
183
}
184
185
method_t m_pMethod;
186
};
187
188
template <class T, class P>
189
struct MemberChannel1 : public Channel1Base<P>
190
{
191
typedef Channel1Base<P>::is_member_t member_spec_t;
192
typedef void (T:: *method_t)(P);
193
194
MemberChannel1(T *pUser, method_t pMethod) : m_pOwner(pUser), m_pMethod(pMethod)
{}
195
~MemberChannel1()
{}
196
virtual void invoke(P p)
197
{
198
if (m_pOwner && m_pMethod)
199
{
200
(m_pOwner->* m_pMethod)(p);
201
}
202
}
203
204
virtual bool equal(Channel1Base<P> *pOther)
205
{
206
MemberChannel1<T, P> *pTmp = dynamic_cast<MemberChannel1<T, P> *>(pOther);
207
if(!pTmp)
208
{
209
return false;
210
}
211
212
return (m_pOwner == pTmp->m_pOwner) && (m_pMethod == pTmp->m_pMethod);
213
}
214
215
virtual bool isOwner(CanUseTrigger *pCandidate)
216
{
217
return m_pOwner == pCandidate;
218
}
219
220
T *m_pOwner;
221
method_t m_pMethod;
222
};
223
224
template <class P1, class P2>
225
struct NakedChannel2 : public Channel2Base<P1, P2>
226
{
227
typedef Channel2Base<P1, P2>::is_not_member_t member_spec_t;
228
typedef void (* method_t)(P1, P2);
229
230
NakedChannel2(method_t pMethod) : m_pMethod(pMethod)
{}
231
~NakedChannel2()
{}
232
virtual void invoke(P1 p1, P2 p2)
233
{
234
if (m_pMethod)
235
{
236
m_pMethod(p1, p2);
237
}
238
}
239
240
virtual bool equal(Channel2Base<P1, P2> *pOther)
241
{
242
NakedChannel2<P1, P2> *pTmp = dynamic_cast<NakedChannel2<P1, P2> *>(pOther);
243
if (!pTmp)
244
{
245
return false;
246
}
247
248
return m_pMethod == pTmp->m_pMethod;
249
}
250
251
virtual bool isOwner(CanUseTrigger *pCandidate)
252
{
253
return false;
254
}
255
256
method_t m_pMethod;
257
};
258
259
template <class T, class P1, class P2>
260
struct MemberChannel2 : public Channel2Base<P1, P2>
261
{
262
typedef Channel2Base<P1, P2>::is_member_t member_spec_t;
263
typedef void (T:: *method_t)(P1, P2);
264
265
MemberChannel2(T *pUser, method_t pMethod) : m_pOwner(pUser), m_pMethod(pMethod)
{}
266
~MemberChannel2()
{}
267
virtual void invoke(P1 p1, P2 p2)
268
{
269
if (m_pOwner && m_pMethod)
270
{
271
(m_pOwner->* m_pMethod)(p1, p2);
272
}
273
}
274
275
virtual bool equal(Channel2Base<P1, P2> *pOther)
276
{
277
MemberChannel2<T, P1, P2> *pTmp = dynamic_cast<MemberChannel2<T, P1, P2> *>(pOther);
278
if (!pTmp)
279
{
280
return false;
281
}
282
283
return (m_pOwner == pTmp->m_pOwner) && (m_pMethod == pTmp->m_pMethod);
284
}
285
286
virtual bool isOwner(CanUseTrigger *pCandidate)
287
{
288
return m_pOwner == pCandidate;
289
}
290
291
T *m_pOwner;
292
method_t m_pMethod;
293
};
294
295
private:
296
std::vector<Relationship> m_vRelationships;
297
};
298
299
template <class Param>
300
class Trigger1 : public TriggerBase
301
{
302
public:
303
~Trigger1()
304
{
305
for (std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
306
it != m_vChannels.end();
307
it++)
308
{
309
delete (*it);
310
}
311
}
312
313
virtual void receiveDestroyMessage(BaseObject *pNotifier)
314
{
315
CanUseTrigger *pTmp = dynamic_cast<CanUseTrigger *>(pNotifier);
316
if (!pTmp)
317
{
318
return;
319
}
320
321
for (std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
322
it != m_vChannels.end();
323
)
324
{
325
CanUseTrigger *pTmp = dynamic_cast<CanUseTrigger *>(pNotifier);
326
if (pTmp && (*it)->isOwner(pTmp))
327
{
328
it = m_vChannels.erase(it);
329
}
330
else
331
{
332
++it;
333
}
334
}
335
336
TriggerBase::receiveDestroyMessage(pNotifier);
337
}
338
339
void fire(Param p)
340
{
341
for (std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
342
it != m_vChannels.end();
343
it++)
344
{
345
(*it)->invoke(p);
346
}
347
}
348
349
void add(void (* pMethod)(Param))
350
{
351
Channel1Base<Param> *pChannel = new NakedChannel1<Param>(pMethod);
352
std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
353
354
for (; it != m_vChannels.end(); it++)
355
{
356
if ((*it)->equal(pChannel))
357
{
358
break;
359
}
360
}
361
362
if (it != m_vChannels.end())
363
{
364
m_vChannels.push_back(pChannel);
365
}
366
}
367
368
template <class TUser>
369
void add(TUser *pUser, void (TUser:: *pMethod)(Param))
370
{
371
//assert(dynamic_cast<CanUseTrigger *>(pUser));
372
373
Channel1Base<Param> *pChannel = new MemberChannel1<TUser, Param>(pUser, pMethod);
374
std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
375
376
for (; it != m_vChannels.end(); it++)
377
{
378
if ((*it)->equal(pChannel))
379
{
380
break;
381
}
382
}
383
384
if (it == m_vChannels.end())
385
{
386
m_vChannels.push_back(pChannel);
387
regToUser(pUser);
388
}
389
}
390
391
void dec(void (* pMethod)(Param))
392
{
393
NakedChannel1<Param> TempChannel(pMethod);
394
std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
395
396
for (; it != m_vChannels.end(); it++)
397
{
398
if ((*it)->equal(&TempChannel))
399
{
400
break;
401
}
402
}
403
404
if (it != m_vChannels.end())
405
{
406
m_vChannels.erase(it);
407
}
408
}
409
410
template <class TUser>
411
void dec(TUser *pUser, void (TUser:: *pMethod)(Param))
412
{
413
std::assert(dynamic_cast<CanUseTrigger *>(pUser));
414
415
MemberChannel1<TUser, Param> TempChannel(pMethod);
416
std::vector<Channel1Base<Param> *>::iterator it = m_vChannels.begin();
417
418
for (; it != m_vChannels.end(); it++)
419
{
420
if ((*it)->equal(&TempChannel))
421
{
422
break;
423
}
424
}
425
426
if (it != m_vChannels.end())
427
{
428
m_vChannels.erase(it);
429
430
}
431
}
432
433
private:
434
std::vector<Channel1Base<Param> *> m_vChannels;
435
};
436
437
template <class Param1, class Param2>
438
class Trigger2 : public TriggerBase
439
{
440
public:
441
~Trigger2()
442
{
443
for (std::vector<Channel2Base<Param1, Param2> *>::iterator it = m_vChannels.begin();
444
it != m_vChannels.end();
445
it++)
446
{
447
delete (*it);
448
}
449
}
450
451
virtual void receiveDestroyMessage(BaseObject *pNotifier)
452
{
453
CanUseTrigger *pTmp = dynamic_cast<CanUseTrigger *>(pNotifier);
454
if (!pTmp)
455
{
456
return;
457
}
458
459
for (std::vector<Channel2Base<Param1, Param2> *>::iterator it = m_vChannels.begin();
460
it != m_vChannels.end();
461
)
462
{
463
CanUseTrigger *pTmp = dynamic_cast<CanUseTrigger *>(pNotifier);
464
if (pTmp && (*it)->isOwner(pTmp))
465
{
466
it = m_vChannels.erase(it);
467
}
468
else
469
{
470
++it;
471
}
472
}
473
474
TriggerBase::receiveDestroyMessage(pNotifier);
475
}
476
477
void fire(Param1 p1, Param2 p2)
478
{
479
for (std::vector<Channel2Base<Param1, Param2> *>::iterator it
480
= m_vChannels.begin();
481
it != m_vChannels.end();
482
it++)
483
{
484
(*it)->invoke(p1, p2);
485
}
486
}
487
488
void add(void (* pMethod)(Param1, Param2))
489
{
490
Channel2Base<Param1, Param2> *pChannel
491
= new NakedChannel2<Param1, Param2>(pMethod);
492
std::vector<Channel2Base<Param1, Param2> *>::iterator it = m_vChannels.begin();
493
494
for (; it != m_vChannels.end(); it++)
495
{
496
if ((*it)->equal(pChannel))
497
{
498
break;
499
}
500
}
501
502
if (it == m_vChannels.end())
503
{
504
m_vChannels.push_back(pChannel);
505
}
506
}
507
508
template <class TUser>
509
void add(TUser *pUser, void (TUser:: *pMethod)(Param1, Param2))
510
{
511
//std::assert(dynamic_cast<CanUseTrigger *>(pUser));
512
513
Channel2Base<Param1, Param2> *pChannel
514
= new MemberChannel2<TUser, Param1, Param2>(pUser, pMethod);
515
std::vector<Channel2Base<Param1, Param2> *>::iterator it = m_vChannels.begin();
516
517
for (; it != m_vChannels.end(); it++)
518
{
519
if ((*it)->equal(pChannel))
520
{
521
break;
522
}
523
}
524
525
if (it == m_vChannels.end())
526
{
527
m_vChannels.push_back(pChannel);
528
regToUser(pUser);
529
}
530
}
531
532
void dec(void (* pMethod)(Param1, Param2))
533
{
534
NakedChannel2<Param1, Param2> TempChannel(pMethod);
535
std::vector<Channel2Base<Param1, Param2> *>::iterator it = m_vChannels.begin();
536
537
for (; it != m_vChannels.end(); it++)
538
{
539
if ((*it)->equal(&TempChannel))
540
{
541
break;
542
}
543
}
544
545
if (it != m_vChannels.end())
546
{
547
m_vChannels.erase(it);
548
}
549
}
550
551
template <class TUser>
552
void dec(TUser *pUser, void (TUser:: *pMethod)(Param1, Param2))
553
{
554
std::assert(dynamic_cast<CanUseTrigger *>(pUser));
555
556
MemberChannel2<TUser, Param1, Param2> TempChannel(pMethod);
557
std::vector<Channel2Base<Param1, Param2> *>::iterator it = m_vChannels.begin();
558
559
for (; it != m_vChannels.end(); it++)
560
{
561
if ((*it)->equal(&TempChannel))
562
{
563
break;
564
}
565
}
566
567
if (it != m_vChannels.end())
568
{
569
m_vChannels.erase(it);
570
571
}
572
}
573
574
private:
575
std::vector<Channel2Base<Param1, Param2> *> m_vChannels;
576
};
577
一些讨论:
1. 为了防止对象析构后其方法被调用,我采用了一个公用的基类实现析构之前的互相通知,这要求相关的类都要继承自CanUseTrigger类,这显然是个“不情之请”,人家本来就有公用基类怎么办?
2. 使用这个机制的代码大概是这样:
class Foo : public CanUseTrigger
{
public:
void doSomething(int i){/*...*/}
};
class Bar
{
public:
Trigger<int> OnSomeEvent;
void someEvent()
{
this->OnSomeEvent.fire(0);
}
};
int main()
{
Foo f;
Bar b;
b.OnSomeEvent.add(&f, &Foo::doSomething);
b.someEvent();
}
注意绿色的那行,我要是不知道f的类型怎么办?这种情况在OO编程中太常见了。事实上大部分基于模板的解决方式(至少是我见过的)都存在这个问题。怎么解决?我也不知道,maybe, We need typeof.
4. 真实世界中的事件回调,还有一个强大的boost::slot,连仿函数都封装进去了。
5. 各种C++事件回调机制中,VCL的方法可能是最方便的,但是只有borland的编译器才能识别__closure关键字。MFC的(其实就是windows的)方法可能是最灵活的,你可以轻松的实现一个线程到另一个线程的回调,这有时候非常有用,尤其是需要将现成的库中异步调用转换成同步调用时。
posted on 2009-07-26 22:59
欲三更 阅读(1881)
评论(11) 编辑 收藏
引用
<!---->
评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论[未登录]
2009-07-27 12:18 Chen Jiecao
这种折叠式的代码怎么搞?不知道cppblog该怎么设置 回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-27 13:56 欲三更
@Chen Jiecao不是有“插入代码”那个按钮吗? 回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-27 21:00 CY
你提到的第1点,在你代码中,是不是由那个CanUseTrigger类里面提到的 m_vRelateList进行管理的,是一个静态成员对象,放了所有绑定的内容列表?然后有对象析构时,就自动检查列表的内容? 回复 更多评论# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-28 04:47 欲三更
@CY哪有static对象啊?是std::,看花眼了吧?
回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-28 04:49 欲三更
@CY简单的说就是Trigger和CanUseTrigger都保存着与它发生关系的对象列表,然后析构时就通知对方,就是这样。 回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-28 10:21 CY
哦,明白了。之前想过要自动解除绑定,就需要一种管理类,管理类不要被使用者看到,就实现在基类中,用一个静态容器。
看你代码到看到一点这样的迹象,以为和我想的一样就没有继续看了~ 回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-28 16:17 没意思
VC里面可以用__hook关键字,委托在编译器层面就是很容易实现的事情建议看看fastdelegate和functor,楼主这个版本,额。。。 回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-28 20:28 欲三更
@没意思我看过fastdelegate,包括作者那篇文章。
这方面各种各样的实现有,从boost的大一统封装,到跟C++基本上已经没有关系的thunk技术。我写这个东西的出发点主要就是不用高级复杂的技术和编译器特性,尽量在直白简单的层次上实现这个功能,至于成品的成色...我工作中用C++ Builder,有__closure关键字可用。
而且就纯C++内部来说,我倾向于“委托”这个东西接收的应该是个完成特定功能的接口指针而不是符合某种签名函数。 回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-29 17:26 DraZet
崩溃了,这么长的代码一行注释都没有,看得难受,建议把注释加上去,方便大家理解 回复 更多评论# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论
2009-07-31 16:56 yisa
恩 做法还是蛮通用的建议:
1. del监听代价太大, 应该用双向链表来自动提供节点解除
2. 可以强化Channel的设计, 减少消息发送者的内容
比如:
Struct CallbackNode
{
~CallbackNode(){Detach();}
void Detach(){...自动从自己挂接的地方(一个双向链表)中移除...}
virtural void Exec()= 0;
Callee* _callee;
Func _callbackFunc;
额外信息: 需要记录自己在双向链表中的前后节点
}
typedef doubleList<CallbackNode> _DList;
class ViCaller
{
void Invoke();
DList _channel;
}
每个CallbackNode可以交给每个监听者callee去管理,
如果 caller先析构, DList 可以自动解掉DList 的Node
如果 监听者callee先析构, Node需要被析构, 这样自动从DList 解除, 自然更不会发生回调
在下愚见
QQ 348360855 yisa
回复 更多评论
# re: 好吧,又一种C++事件回调封装以及相关的零碎讨论[未登录]
2009-08-03 14:32 欲三更
@yisa嗯,这个做法确实避免了“关系”的冗余。 回复 更多评论
刷新评论列表
找优秀程序员,就在博客园 IT新闻: · Linux Mint将引入Gnome 3 · 分析师预计诺基亚第三季度或净亏3.3亿美元 · 谷歌与新泽西公交合作推广Google Wallet · 百度获发改委云计算专项最高激励:腾讯阿里其次 · 三星:Galaxy Nexus为回避苹果专利特别设计 |
上一篇:Review of Operating Systems
下一篇:异步消息的传递-回调机制
相关文章推荐
- 好吧,又一种C++事件回调封装以及相关的零碎讨论
- Android--焦点问题以及讨论事件传递机制问题(结合部分相关源码)
- 【事件驱动】【数码管识别】(C++动态链接库的封装和调用)
- C++ 模板特化以及Typelist的相关理解
- C/C++中的const 与#define的使用疑问与异同以及相关用法
- c#调用C++的DLL找不到入口点以及衍生的相关问题
- cocos2d-x中函数回调 事件监听机制详细解析 涉及c++成员函数指针
- 我的Node.js学习之路(三)--node.js作用、回调、同步和异步代码 以及事件循环
- 事件冒泡机制和事件委派 以及回调的匿名函数参数
- CRT,C++运行时库详解(历史脉络以及相关名称定义和区别)
- React Native原生UI封装以及事件处理
- 关于JAVA匿名内部类,回调,事件模式的一点讨论
- .Net/C# 封装磁盘目录文件搜索功能的工具类 (实现了与搜索相关的事件,以便插入客户处理代码)
- 关于C++: try...catch...的汇编实现&相关讨论,FS
- C++封装常用对象和对头文件以及预编译机制的探索
- IEngineEditor与IWorkspaceEdit,以及相关的事件监听
- 页面中插入flash,并且给flash添加单击事件控制播放,以及获取相关参数.
- 关于Web开发里并发、同步、异步以及事件驱动编程的相关技术
- React Native原生UI封装以及事件处理
- c++ 封装哈希表(Hash) 以及实现迭代器(iterator)