-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\r
-<!-- saved from url=(0045)http://cscene.org/topics/misc/cs7-04.xml.html -->\r
-<HTML><HEAD><TITLE>Callbacks in C++: The OO Way</TITLE>\r
-<META content="text/html; charset=iso-8859-1" http-equiv=Content-Type><LINK \r
-href="default.css" rel=stylesheet \r
-type=text/css>\r
-<META content="MSHTML 5.00.2920.0" name=GENERATOR></HEAD>\r
-<BODY>\r
-<TABLE width="100%">\r
- <TBODY>\r
- <TR>\r
- <TD><A href="http://cscene.org/"><IMG alt="cscene logo" border=0 \r
- class=logo height=52 src="logo.gif" \r
- width=120></A></TD>\r
- <TD align=middle width="100%"><A \r
- href="http://cscene.org/index.html">C-Scene</A> <A \r
- href="http://cscene.org/issues.html">Issues 1..9</A> <A \r
- href="http://cscene.org/bios.html">Authors</A> <BR><A \r
- href="http://cscene.org/topics/algo.xml.html">Algorithms</A> <A \r
- href="http://cscene.org/topics/books.xml.html">Books</A> <A \r
- href="http://cscene.org/topics/patterns.xml.html">Patterns</A> <A \r
- href="http://cscene.org/topics/graphics.xml.html">Graphics</A> <A \r
- href="http://cscene.org/topics/misc.xml.html">Miscellaneous</A> <A \r
- href="http://cscene.org/topics/unix.xml.html">UNIX</A> <A \r
- href="http://cscene.org/topics/web.xml.html">Web & XML</A> <A \r
- href="http://cscene.org/topics/windows.xml.html">Windows</A> <BR><A \r
- href="http://cscene.org/feedback.html">Feedback</A> <A \r
- href="http://cscene.org/faqs.html">FAQs</A> <A \r
- href="http://cscene.org/changes.html">Changes</A> <A \r
- href="http://cscene.org/submit.html">Submissions</A> </TD>\r
- <TD>\r
- <FORM action=http://www.google.com/search method=get><INPUT name=hl \r
- type=hidden value=en><INPUT name=safe type=hidden value=off><INPUT \r
- name=domains type=hidden value=cscene.org><INPUT name=sitesearch \r
- 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
-<H1 class=s1>Callbacks in C++: The OO Way</H1>\r
-<DIV class=toc>\r
-<H2 class=s2 id=N1698364>Content</H2>\r
-<OL>\r
- <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N7895983">What are \r
- Callbacks?</A>\r
- <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N310499">A simple \r
- Thread class</A>\r
- <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N7751758">Thread \r
- implementation</A>\r
- <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N3038356">A real \r
- Thread class</A></LI></OL></DIV>\r
-<DIV class=article>\r
-<P>by <I>Jürgen Hermann</I><BR>last updated <I>2001/08/02</I> (version \r
-<I>1.1.1.1</I>)<BR>also available as <A \r
-href="http://cscene.org/topics/misc/cs7-04.xml.txt">XML</A></P>\r
-<DIV class=s2>\r
-<H2 class=s2 id=N7895983>What are Callbacks?</H2>\r
-<P>Many operating systems and other subsystems (like GUI libraries) feature a \r
-special type of hook into those systems, named callbacks or callback functions. \r
-Upon initialization or by calling an API function you pass pointers to the \r
-callback into the subsystem, for later use. The problem with those functions is, \r
-since these subsystems are nowadays not yet OO, that they have no notion of what \r
-an object is. So if you want to have a <EM>callback object </EM> instead of \r
-a mere function, some OO magic is called for.</P>\r
-<P>As an example, consider the <CODE>BeginThread</CODE> API that many OSes have \r
-in a quite similar form; we assume that it takes a pointer to the function that \r
-provides the actual code for the newly created thread plus a <EM>data \r
-pointer </EM> that is passed to that function as a startup parameter. Thus, \r
-we end up with <CODE>BeginThread (void (*thread_func) (void*), void* \r
-startup_data)</CODE>. Now let's make a <CODE>Thread</CODE> class of \r
-that.</P></DIV>\r
-<DIV class=s2>\r
-<H2 class=s2 id=N310499>A simple Thread class</H2>\r
-<P>What we want to have is an ABC (abstract base class) that you can inherit \r
-from, creating specialized thread classes and in turn thread objects (i.e. \r
-actual threads). The code of the thread is located in a <EM>pure \r
-virtual </EM> function <CODE>code</CODE> that is provided by the inherited \r
-class. <CODE>code</CODE> is then similar to the <CODE>thread_func</CODE> \r
-parameter of the <CODE>BeginThread</CODE> call, but is a full-blown member \r
-function, not just a C function. So, we get this interface for the \r
-<CODE>Thread</CODE> class:</P><PRE class=source>class Thread {\r
-public:\r
- virtual ~Thread();\r
- void run();\r
-\r
-protected:\r
- Thread();\r
- virtual void code() = 0;\r
-\r
-private:\r
- int running;\r
-\r
- static void dispatch(void* thread_obj);\r
-};\r
-</PRE>\r
-<P>This might seem quite unusual to you (like having a protected constructor), \r
-but things will be explained in due course.</P></DIV>\r
-<DIV class=s2>\r
-<H2 class=s2 id=N7751758>Thread implementation</H2>\r
-<P>When we put the thread concept into a class, we have to consider lifetime. A \r
-thread exists as long as the thread function does not return, thus the object \r
-has to have the same lifetime. Because of this, an <CODE>auto</CODE> thread \r
-object does not make much sense; we insure that every thread object exists on \r
-the heap by making the ctor protected and providing a static factory method \r
-<CODE>create</CODE> for thread objects in each derived class:</P><PRE class=source>Thread::Thread() \r
- : running(0) \r
-{\r
-}\r
-\r
-DerivedThread& DerivedThread::create()\r
-{\r
- return *new DerivedThread;\r
-}\r
-</PRE>\r
-<P><CODE>create</CODE> has to be added to every inherited class, returning an \r
-object of that class.</P>\r
-<P>Next, we need a <CODE>run</CODE> method that actually starts the thread. This \r
-can't be done in the ctor: when <CODE>code</CODE> would be registered as a \r
-thread of execution in the <EM>base class </EM> ctor, the superclass would \r
-not yet be fully created and calling <CODE>code</CODE> would be quite invalid \r
-and dangerous. <CODE>run</CODE> does its job by registering the \r
-<CODE>dispatch</CODE> function as a thread, giving that thread the object \r
-pointer as a startup parameter; since <CODE>dispatch</CODE> is static, it has a \r
-prototype that matches the <CODE>void(*)(void*)</CODE> parameter of \r
-<CODE>BeginThread</CODE>.</P><PRE class=source>void Thread::run()\r
-{\r
- // Don't start two threads on the same object\r
- if (running) return;\r
-\r
- // Create an OS thread, using the static callback\r
- BeginThread(Thread::dispatch, this);\r
- running = 1;\r
-}\r
-</PRE>\r
-<P>So finally, <CODE>dispatch</CODE> is called and performs the step from a \r
-procedural callback to the callback object:</P><PRE class=source>void Thread::dispatch(void* thread_obj)\r
-{\r
- // Call the actual OO thread code\r
- ((Thread*)thread_obj)->code();\r
-\r
- // After code() returns, kill the thread object\r
- delete (Thread*)thread_obj;\r
-}\r
-</PRE></DIV>\r
-<DIV class=s2>\r
-<H2 class=s2 id=N3038356>A real Thread class</H2>\r
-<P>A real-world thread class has to consider a few things we have ignored here. \r
-These things include: </P>\r
-<OL class=userlist>\r
- <LI>more access to the thread data, like a method giving the thread ID. \r
- <LI>a method for killing the thread, including the deletion of the thread \r
- object. </LI></OL>\r
-<P>Developed and tested on Windows NT, this is the <A \r
-href="http://cscene.org/archives/callback.cpp.txt">source</A> for a little \r
-example program that implements the above in the real world. If you run it, you \r
-get something similar to the following output:</P><PRE class=source>[\cscene\callback]callback\r
-Started thread #80 for dice1\r
-Started thread #84 for dice2\r
-dice1 rolled 1\r
-dice2 rolled 1\r
-dice2 rolled 3\r
-dice1 rolled 1\r
-dice2 rolled 4\r
-dice1 rolled 6\r
-dice1 rolled 3\r
-dice2 rolled 3\r
-dice1 rolled 1\r
-dice1 rolled 4\r
-dice2 rolled 4\r
-dice2 rolled 3\r
-dice1 rolled 1\r
-dice2 rolled 6\r
-dice1 rolled 2\r
-dice2 rolled 2\r
-dice1 rolled 1\r
-dice2 rolled 4\r
-dice2 rolled 1\r
-dice1 rolled 4\r
-dice1 rolled 5\r
-dice2 rolled 1\r
-dice1 rolled 3\r
-dice2 rolled 3\r
-dice1 rolled 2\r
-dice1 rolled 6\r
-dice2 rolled 2\r
-dice1 rolled 1\r
-dice2 rolled 3\r
-dice1 rolled 4\r
-dice1 rolled 5\r
-dice2 rolled 3\r
-dice2 rolled 6\r
-dice1 rolled 4\r
-</PRE>\r
-<P>Have fun!</P></DIV>\r
-<P><SMALL><EM>This article is Copyright © 1997-98 by Jürgen Hermann<BR>and \r
-Copyright © 1999 by C-Scene. All Rights Reserved. </EM></SMALL></P></DIV>\r
-<DIV class=lastwords>\r
-<HR>\r
-Copyright © 1997-2000 by C-Scene. All Rights Reserved.<BR><BR>Part of the \r
-graphics and stylesheets used to generate this site are<BR>Copyright © 1999-2000 \r
-by Apache Software Foundation. </DIV></BODY></HTML>\r
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!-- saved from url=(0045)http://cscene.org/topics/misc/cs7-04.xml.html -->
+<HTML><HEAD><TITLE>Callbacks in C++: The OO Way</TITLE>
+<META content="text/html; charset=iso-8859-1" http-equiv=Content-Type><LINK
+href="default.css" rel=stylesheet
+type=text/css>
+<META content="MSHTML 5.00.2920.0" name=GENERATOR></HEAD>
+<BODY>
+<TABLE width="100%">
+ <TBODY>
+ <TR>
+ <TD><A href="http://cscene.org/"><IMG alt="cscene logo" border=0
+ class=logo height=52 src="logo.gif"
+ width=120></A></TD>
+ <TD align=middle width="100%"><A
+ href="http://cscene.org/index.html">C-Scene</A> <A
+ href="http://cscene.org/issues.html">Issues 1..9</A> <A
+ href="http://cscene.org/bios.html">Authors</A> <BR><A
+ href="http://cscene.org/topics/algo.xml.html">Algorithms</A> <A
+ href="http://cscene.org/topics/books.xml.html">Books</A> <A
+ href="http://cscene.org/topics/patterns.xml.html">Patterns</A> <A
+ href="http://cscene.org/topics/graphics.xml.html">Graphics</A> <A
+ href="http://cscene.org/topics/misc.xml.html">Miscellaneous</A> <A
+ href="http://cscene.org/topics/unix.xml.html">UNIX</A> <A
+ href="http://cscene.org/topics/web.xml.html">Web & XML</A> <A
+ href="http://cscene.org/topics/windows.xml.html">Windows</A> <BR><A
+ href="http://cscene.org/feedback.html">Feedback</A> <A
+ href="http://cscene.org/faqs.html">FAQs</A> <A
+ href="http://cscene.org/changes.html">Changes</A> <A
+ href="http://cscene.org/submit.html">Submissions</A> </TD>
+ <TD>
+ <FORM action=http://www.google.com/search method=get><INPUT name=hl
+ type=hidden value=en><INPUT name=safe type=hidden value=off><INPUT
+ name=domains type=hidden value=cscene.org><INPUT name=sitesearch
+ type=hidden value=cscene.org><INPUT maxLength=256 name=q size=31><INPUT name=btnG type=submit value=Search></FORM></TD></TR></TBODY></TABLE>
+<H1 class=s1>Callbacks in C++: The OO Way</H1>
+<DIV class=toc>
+<H2 class=s2 id=N1698364>Content</H2>
+<OL>
+ <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N7895983">What are
+ Callbacks?</A>
+ <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N310499">A simple
+ Thread class</A>
+ <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N7751758">Thread
+ implementation</A>
+ <LI><A href="http://cscene.org/topics/misc/cs7-04.xml.html#N3038356">A real
+ Thread class</A></LI></OL></DIV>
+<DIV class=article>
+<P>by <I>Jürgen Hermann</I><BR>last updated <I>2001/08/02</I> (version
+<I>1.1.1.1</I>)<BR>also available as <A
+href="http://cscene.org/topics/misc/cs7-04.xml.txt">XML</A></P>
+<DIV class=s2>
+<H2 class=s2 id=N7895983>What are Callbacks?</H2>
+<P>Many operating systems and other subsystems (like GUI libraries) feature a
+special type of hook into those systems, named callbacks or callback functions.
+Upon initialization or by calling an API function you pass pointers to the
+callback into the subsystem, for later use. The problem with those functions is,
+since these subsystems are nowadays not yet OO, that they have no notion of what
+an object is. So if you want to have a <EM>callback object </EM> instead of
+a mere function, some OO magic is called for.</P>
+<P>As an example, consider the <CODE>BeginThread</CODE> API that many OSes have
+in a quite similar form; we assume that it takes a pointer to the function that
+provides the actual code for the newly created thread plus a <EM>data
+pointer </EM> that is passed to that function as a startup parameter. Thus,
+we end up with <CODE>BeginThread (void (*thread_func) (void*), void*
+startup_data)</CODE>. Now let's make a <CODE>Thread</CODE> class of
+that.</P></DIV>
+<DIV class=s2>
+<H2 class=s2 id=N310499>A simple Thread class</H2>
+<P>What we want to have is an ABC (abstract base class) that you can inherit
+from, creating specialized thread classes and in turn thread objects (i.e.
+actual threads). The code of the thread is located in a <EM>pure
+virtual </EM> function <CODE>code</CODE> that is provided by the inherited
+class. <CODE>code</CODE> is then similar to the <CODE>thread_func</CODE>
+parameter of the <CODE>BeginThread</CODE> call, but is a full-blown member
+function, not just a C function. So, we get this interface for the
+<CODE>Thread</CODE> class:</P><PRE class=source>class Thread {
+public:
+ virtual ~Thread();
+ void run();
+
+protected:
+ Thread();
+ virtual void code() = 0;
+
+private:
+ int running;
+
+ static void dispatch(void* thread_obj);
+};
+</PRE>
+<P>This might seem quite unusual to you (like having a protected constructor),
+but things will be explained in due course.</P></DIV>
+<DIV class=s2>
+<H2 class=s2 id=N7751758>Thread implementation</H2>
+<P>When we put the thread concept into a class, we have to consider lifetime. A
+thread exists as long as the thread function does not return, thus the object
+has to have the same lifetime. Because of this, an <CODE>auto</CODE> thread
+object does not make much sense; we insure that every thread object exists on
+the heap by making the ctor protected and providing a static factory method
+<CODE>create</CODE> for thread objects in each derived class:</P><PRE class=source>Thread::Thread()
+ : running(0)
+{
+}
+
+DerivedThread& DerivedThread::create()
+{
+ return *new DerivedThread;
+}
+</PRE>
+<P><CODE>create</CODE> has to be added to every inherited class, returning an
+object of that class.</P>
+<P>Next, we need a <CODE>run</CODE> method that actually starts the thread. This
+can't be done in the ctor: when <CODE>code</CODE> would be registered as a
+thread of execution in the <EM>base class </EM> ctor, the superclass would
+not yet be fully created and calling <CODE>code</CODE> would be quite invalid
+and dangerous. <CODE>run</CODE> does its job by registering the
+<CODE>dispatch</CODE> function as a thread, giving that thread the object
+pointer as a startup parameter; since <CODE>dispatch</CODE> is static, it has a
+prototype that matches the <CODE>void(*)(void*)</CODE> parameter of
+<CODE>BeginThread</CODE>.</P><PRE class=source>void Thread::run()
+{
+ // Don't start two threads on the same object
+ if (running) return;
+
+ // Create an OS thread, using the static callback
+ BeginThread(Thread::dispatch, this);
+ running = 1;
+}
+</PRE>
+<P>So finally, <CODE>dispatch</CODE> is called and performs the step from a
+procedural callback to the callback object:</P><PRE class=source>void Thread::dispatch(void* thread_obj)
+{
+ // Call the actual OO thread code
+ ((Thread*)thread_obj)->code();
+
+ // After code() returns, kill the thread object
+ delete (Thread*)thread_obj;
+}
+</PRE></DIV>
+<DIV class=s2>
+<H2 class=s2 id=N3038356>A real Thread class</H2>
+<P>A real-world thread class has to consider a few things we have ignored here.
+These things include: </P>
+<OL class=userlist>
+ <LI>more access to the thread data, like a method giving the thread ID.
+ <LI>a method for killing the thread, including the deletion of the thread
+ object. </LI></OL>
+<P>Developed and tested on Windows NT, this is the <A
+href="../../tests/oo_thread.cpp">source</A> for a little
+example program that implements the above in the real world. If you run it, you
+get something similar to the following output:</P><PRE class=source>[\cscene\callback]callback
+Started thread #80 for dice1
+Started thread #84 for dice2
+dice1 rolled 1
+dice2 rolled 1
+dice2 rolled 3
+dice1 rolled 1
+dice2 rolled 4
+dice1 rolled 6
+dice1 rolled 3
+dice2 rolled 3
+dice1 rolled 1
+dice1 rolled 4
+dice2 rolled 4
+dice2 rolled 3
+dice1 rolled 1
+dice2 rolled 6
+dice1 rolled 2
+dice2 rolled 2
+dice1 rolled 1
+dice2 rolled 4
+dice2 rolled 1
+dice1 rolled 4
+dice1 rolled 5
+dice2 rolled 1
+dice1 rolled 3
+dice2 rolled 3
+dice1 rolled 2
+dice1 rolled 6
+dice2 rolled 2
+dice1 rolled 1
+dice2 rolled 3
+dice1 rolled 4
+dice1 rolled 5
+dice2 rolled 3
+dice2 rolled 6
+dice1 rolled 4
+</PRE>
+<P>Have fun!</P></DIV>
+<P><SMALL><EM>This article is Copyright © 1997-98 by Jürgen Hermann<BR>and
+Copyright © 1999 by C-Scene. All Rights Reserved. </EM></SMALL></P></DIV>
+<DIV class=lastwords>
+<HR>
+Copyright © 1997-2000 by C-Scene. All Rights Reserved.<BR><BR>Part of the
+graphics and stylesheets used to generate this site are<BR>Copyright © 1999-2000
+by Apache Software Foundation. </DIV></BODY></HTML>
--- /dev/null
+// Callbacks in C++
+// Refer to http://www.cscene.org for detailed docs.
+//
+// Copyright (c) 1998 by Juergen Hermann
+//
+// To make things easy, we put all code in one file.
+// Large parts of this should go to thread.hpp/cpp in real real life.
+
+#include <cstdlib>
+#include <ctime>
+#include <cassert>
+#include <string>
+#include <iostream>
+#include <pthread.h>
+#include <unistd.h>
+
+///////////////////////////////////////////////////////////////////////
+// Utility stuff
+///////////////////////////////////////////////////////////////////////
+
+inline int randrange(int lo, int hi)
+{
+ return rand() / (RAND_MAX / (hi - lo + 1)) + lo;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// A thread base class
+///////////////////////////////////////////////////////////////////////
+
+class Thread {
+public:
+ virtual ~Thread() {}
+ void run();
+ pthread_t get_thread() const { return thread; }
+
+protected:
+ Thread();
+ virtual void code() = 0;
+
+private:
+ int running;
+ pthread_t thread;
+
+ static void* dispatch(void* thread_obj);
+};
+
+
+Thread::Thread()
+ : running(0)
+{
+}
+
+
+void Thread::run()
+{
+ // Don't start two threads on the same object
+ if (running) return;
+
+ // Create an OS thread, using the static callback
+ assert(!pthread_create(&thread, NULL, Thread::dispatch, static_cast<void*>(this)));
+ pthread_detach(thread);
+ running = 1;
+}
+
+
+void* Thread::dispatch(void* thread_obj)
+{
+ // Call the actual OO thread code
+ static_cast<Thread*>(thread_obj)->code();
+
+ // After code() returns, kill the thread object
+ delete static_cast<Thread*>(thread_obj);
+
+ return NULL;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// An example thread class
+///////////////////////////////////////////////////////////////////////
+
+class Dice : private Thread {
+public:
+ static void Dice::create(const char* dicename);
+ //static CRITICAL_SECTION sync;
+
+private:
+ Dice(const char* dicename) : name(dicename) {}
+ virtual void code();
+
+ std::string name;
+};
+
+
+//CRITICAL_SECTION Dice::sync;
+
+
+void Dice::create(const char* dicename)
+{
+ (new Dice(dicename))->run();
+}
+
+
+void Dice::code()
+{
+ //EnterCriticalSection(&sync);
+ std::cout << "Started thread #" << static_cast<unsigned long>(get_thread())
+ << " for " << name << std::endl;
+ //LeaveCriticalSection(&sync);
+
+ srand(time(0) * get_thread());
+
+ for (;;) {
+ int val = randrange(1, 6);
+
+ //EnterCriticalSection(&sync);
+ std::cout << name << " rolled " << val << std::endl;
+ //LeaveCriticalSection(&sync);
+
+ sleep(randrange(0, 1)); // wait up to 1 sec
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// Let's test!
+///////////////////////////////////////////////////////////////////////
+
+int main()
+{
+ //InitializeCriticalSection(&Dice::sync);
+
+ Dice::create("dice1");
+ Dice::create("dice2");
+
+ sleep(10); // roll dice for 10 seconds
+
+ return 0; // cleanup? what's cleanup?
+}