1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
\r
2 <!-- saved from url=(0045)http://cscene.org/topics/misc/cs7-04.xml.html -->
\r
3 <HTML><HEAD><TITLE>Callbacks in C++: The OO Way</TITLE>
\r
4 <META content="text/html; charset=iso-8859-1" http-equiv=Content-Type><LINK
\r
5 href="default.css" rel=stylesheet
\r
7 <META content="MSHTML 5.00.2920.0" name=GENERATOR></HEAD>
\r
12 <TD><A href="http://cscene.org/"><IMG alt="cscene logo" border=0
\r
13 class=logo height=52 src="logo.gif"
\r
15 <TD align=middle width="100%"><A
\r
16 href="http://cscene.org/index.html">C-Scene</A> <A
\r
17 href="http://cscene.org/issues.html">Issues 1..9</A> <A
\r
18 href="http://cscene.org/bios.html">Authors</A> <BR><A
\r
19 href="http://cscene.org/topics/algo.xml.html">Algorithms</A> <A
\r
20 href="http://cscene.org/topics/books.xml.html">Books</A> <A
\r
21 href="http://cscene.org/topics/patterns.xml.html">Patterns</A> <A
\r
22 href="http://cscene.org/topics/graphics.xml.html">Graphics</A> <A
\r
23 href="http://cscene.org/topics/misc.xml.html">Miscellaneous</A> <A
\r
24 href="http://cscene.org/topics/unix.xml.html">UNIX</A> <A
\r
25 href="http://cscene.org/topics/web.xml.html">Web & XML</A> <A
\r
26 href="http://cscene.org/topics/windows.xml.html">Windows</A> <BR><A
\r
27 href="http://cscene.org/feedback.html">Feedback</A> <A
\r
28 href="http://cscene.org/faqs.html">FAQs</A> <A
\r
29 href="http://cscene.org/changes.html">Changes</A> <A
\r
30 href="http://cscene.org/submit.html">Submissions</A> </TD>
\r
32 <FORM action=http://www.google.com/search method=get><INPUT name=hl
\r
33 type=hidden value=en><INPUT name=safe type=hidden value=off><INPUT
\r
34 name=domains type=hidden value=cscene.org><INPUT name=sitesearch
\r
35 type=hidden value=cscene.org><INPUT maxLength=256 name=q size=31><INPUT name=btnG type=submit value=Search></FORM></TD></TR></TBODY></TABLE>
\r
36 <H1 class=s1>Callbacks in C++: The OO Way</H1>
\r
38 <H2 class=s2 id=N1698364>Content</H2>
\r
40 <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N7895983">What are
\r
42 <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N310499">A simple
\r
44 <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N7751758">Thread
\r
46 <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N3038356">A real
\r
47 Thread class</A></LI></OL></DIV>
\r
49 <P>by <I>Jürgen Hermann</I><BR>last updated <I>2001/08/02</I> (version
\r
50 <I>1.1.1.1</I>)<BR>also available as <A
\r
51 href="http://cscene.org/topics/misc/cs7-04.xml.txt">XML</A></P>
\r
53 <H2 class=s2 id=N7895983>What are Callbacks?</H2>
\r
54 <P>Many operating systems and other subsystems (like GUI libraries) feature a
\r
55 special type of hook into those systems, named callbacks or callback functions.
\r
56 Upon initialization or by calling an API function you pass pointers to the
\r
57 callback into the subsystem, for later use. The problem with those functions is,
\r
58 since these subsystems are nowadays not yet OO, that they have no notion of what
\r
59 an object is. So if you want to have a <EM>callback object </EM> instead of
\r
60 a mere function, some OO magic is called for.</P>
\r
61 <P>As an example, consider the <CODE>BeginThread</CODE> API that many OSes have
\r
62 in a quite similar form; we assume that it takes a pointer to the function that
\r
63 provides the actual code for the newly created thread plus a <EM>data
\r
64 pointer </EM> that is passed to that function as a startup parameter. Thus,
\r
65 we end up with <CODE>BeginThread (void (*thread_func) (void*), void*
\r
66 startup_data)</CODE>. Now let's make a <CODE>Thread</CODE> class of
\r
69 <H2 class=s2 id=N310499>A simple Thread class</H2>
\r
70 <P>What we want to have is an ABC (abstract base class) that you can inherit
\r
71 from, creating specialized thread classes and in turn thread objects (i.e.
\r
72 actual threads). The code of the thread is located in a <EM>pure
\r
73 virtual </EM> function <CODE>code</CODE> that is provided by the inherited
\r
74 class. <CODE>code</CODE> is then similar to the <CODE>thread_func</CODE>
\r
75 parameter of the <CODE>BeginThread</CODE> call, but is a full-blown member
\r
76 function, not just a C function. So, we get this interface for the
\r
77 <CODE>Thread</CODE> class:</P><PRE class=source>class Thread {
\r
84 virtual void code() = 0;
\r
89 static void dispatch(void* thread_obj);
\r
92 <P>This might seem quite unusual to you (like having a protected constructor),
\r
93 but things will be explained in due course.</P></DIV>
\r
95 <H2 class=s2 id=N7751758>Thread implementation</H2>
\r
96 <P>When we put the thread concept into a class, we have to consider lifetime. A
\r
97 thread exists as long as the thread function does not return, thus the object
\r
98 has to have the same lifetime. Because of this, an <CODE>auto</CODE> thread
\r
99 object does not make much sense; we insure that every thread object exists on
\r
100 the heap by making the ctor protected and providing a static factory method
\r
101 <CODE>create</CODE> for thread objects in each derived class:</P><PRE class=source>Thread::Thread()
\r
106 DerivedThread& DerivedThread::create()
\r
108 return *new DerivedThread;
\r
111 <P><CODE>create</CODE> has to be added to every inherited class, returning an
\r
112 object of that class.</P>
\r
113 <P>Next, we need a <CODE>run</CODE> method that actually starts the thread. This
\r
114 can't be done in the ctor: when <CODE>code</CODE> would be registered as a
\r
115 thread of execution in the <EM>base class </EM> ctor, the superclass would
\r
116 not yet be fully created and calling <CODE>code</CODE> would be quite invalid
\r
117 and dangerous. <CODE>run</CODE> does its job by registering the
\r
118 <CODE>dispatch</CODE> function as a thread, giving that thread the object
\r
119 pointer as a startup parameter; since <CODE>dispatch</CODE> is static, it has a
\r
120 prototype that matches the <CODE>void(*)(void*)</CODE> parameter of
\r
121 <CODE>BeginThread</CODE>.</P><PRE class=source>void Thread::run()
\r
123 // Don't start two threads on the same object
\r
124 if (running) return;
\r
126 // Create an OS thread, using the static callback
\r
127 BeginThread(Thread::dispatch, this);
\r
131 <P>So finally, <CODE>dispatch</CODE> is called and performs the step from a
\r
132 procedural callback to the callback object:</P><PRE class=source>void Thread::dispatch(void* thread_obj)
\r
134 // Call the actual OO thread code
\r
135 ((Thread*)thread_obj)->code();
\r
137 // After code() returns, kill the thread object
\r
138 delete (Thread*)thread_obj;
\r
142 <H2 class=s2 id=N3038356>A real Thread class</H2>
\r
143 <P>A real-world thread class has to consider a few things we have ignored here.
\r
144 These things include: </P>
\r
145 <OL class=userlist>
\r
146 <LI>more access to the thread data, like a method giving the thread ID.
\r
147 <LI>a method for killing the thread, including the deletion of the thread
\r
149 <P>Developed and tested on Windows NT, this is the <A
\r
150 href="http://cscene.org/archives/callback.cpp.txt">source</A> for a little
\r
151 example program that implements the above in the real world. If you run it, you
\r
152 get something similar to the following output:</P><PRE class=source>[\cscene\callback]callback
\r
153 Started thread #80 for dice1
\r
154 Started thread #84 for dice2
\r
190 <P>Have fun!</P></DIV>
\r
191 <P><SMALL><EM>This article is Copyright © 1997-98 by Jürgen Hermann<BR>and
\r
192 Copyright © 1999 by C-Scene. All Rights Reserved. </EM></SMALL></P></DIV>
\r
193 <DIV class=lastwords>
\r
195 Copyright © 1997-2000 by C-Scene. All Rights Reserved.<BR><BR>Part of the
\r
196 graphics and stylesheets used to generate this site are<BR>Copyright © 1999-2000
\r
197 by Apache Software Foundation. </DIV></BODY></HTML>
\r