| Home | Forums | Reviews | Guides | Newsgroups | Register | Search |
![]() |
| Thread Tools |
|
Chris M. Thomasson
Guest
Posts: n/a
|
What exactly do you have in mind wrt integrating monitor, when keyword and
active object? How far off the corrected version of my example? http://groups.google.com/group/comp....9f7980d9323edc BTW, sorry for posting the broken version: http://groups.google.com/group/comp....62e9a5d41a6ae6 I jumped the gun! |
|
|
|
|
|||
|
|||
| Chris M. Thomasson |
|
|
|
| |
|
Chris M. Thomasson
Guest
Posts: n/a
|
Given the fixed version I posted here:
http://groups.google.com/group/comp....9f7980d9323edc one can easily create multiple producers and consumers like this: int main(void) { { bounded_buffer<unsigned> b(BOUND); active<producer> p[] = { &b, &b, &b, &b, &b, &b }; active<consumer> c[] = { &b, &b, &b, &b, &b, &b }; } std: std::fflush(stdout); std::getchar(); return 0; } I really do like the convenience of the `active' helper template. Anyway, one caveat wrt the way these specific consumers are coded to act on data, the number of producers and consumers must be equal. |
|
|
|
|
|||
|
|||
| Chris M. Thomasson |
|
|
|
| |
| Chris M. Thomasson |
|
Szabolcs Ferenczi
Guest
Posts: n/a
|
On Oct 31, 10:14*pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
> What exactly do you have in mind wrt integrating monitor, when keyword and > active object? I did not meant integrating all the three but it is an interesting idea too. I meant that if we have high level wrappers for easy coding of the monitor construction, it is possible to put together applications where there are active objects and passive ones for shared data communication around. One such an example is the producers and consumers example. > How far off the corrected version of my example? It is promising. Better than I expected, however, the `broadcast();' should be also included into the RAII (the `when' object). I did not think of using the preprocessor for the task but rather some pure C++ construction. That is why I thought that the RAII object could not only be a simple `lock_guard' but something like a `when_guard'. Then one does not have to explicitly place the `broadcast();' and the `while (! (mp_pred)) this->wait();' can be part of the constructor of the `when_guard'. On the other hand, the preprocessor allows to keep the more natural syntax. Best Regards, Szabolcs |
|
|
|
|
|||
|
|||
| Szabolcs Ferenczi |
|
Chris M. Thomasson
Guest
Posts: n/a
|
"Szabolcs Ferenczi" <> wrote in message news:980d8b20-a9bb-4912-a0fb-... On Oct 31, 10:14 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote: > > What exactly do you have in mind wrt integrating monitor, when keyword > > and > > active object? > I did not meant integrating all the three but it is an interesting > idea too. > I meant that if we have high level wrappers for easy coding of the > monitor construction, it is possible to put together applications > where there are active objects and passive ones for shared data > communication around. One such an example is the producers and > consumers example. > > How far off the corrected version of my example? > It is promising. Better than I expected, however, the `broadcast();' > should be also included into the RAII (the `when' object). Okay. Also, there was a little problem in the when macros. I need another level of indirection in order to properly expand the __LINE__ macro. Here is new version: __________________________________________________ ______________ class monitor { pthread_mutex_t m_mutex; pthread_cond_t m_cond; public: monitor() { pthread_mutex_init(&m_mutex, NULL); pthread_cond_init(&m_cond, NULL); } ~monitor() { pthread_cond_destroy(&m_cond); pthread_mutex_destroy(&m_mutex); } struct lock_guard { monitor& m_monitor; lock_guard(monitor& monitor_) : m_monitor(monitor_) { m_monitor.lock(); } ~lock_guard() { m_monitor.unlock(); } }; struct signal_guard { monitor& m_monitor; bool const m_broadcast; signal_guard(monitor& monitor_, bool broadcast = true) : m_monitor(monitor_), m_broadcast(broadcast) { } ~signal_guard() { if (m_broadcast) { m_monitor.broadcast(); } else { m_monitor.signal(); } } }; void lock() { pthread_mutex_lock(&m_mutex); } void unlock() { pthread_mutex_unlock(&m_mutex); } void wait() { pthread_cond_wait(&m_cond, &m_mutex); } void signal() { pthread_cond_signal(&m_cond); } void broadcast() { pthread_cond_broadcast(&m_cond); } }; #define when_xx(mp_pred, mp_line) \ monitor::lock_guard lock_guard_##mp_line(*this); \ monitor::signal_guard signal_guard_##mp_line(*this); \ while (! (mp_pred)) this->wait(); #define when_x(mp_pred, mp_line) when_xx(mp_pred, mp_line) #define when(mp_pred) when_x(mp_pred, __LINE__) __________________________________________________ ______________ Now it properly expands __LINE__ and it also automatically broadcasts. > I did not think of using the preprocessor for the task but rather some > pure C++ construction. That is why I thought that the RAII object > could not only be a simple `lock_guard' but something like a > `when_guard'. Then one does not have to explicitly place the > `broadcast();' and the `while (! (mp_pred)) this->wait();' can be part > of the constructor of the `when_guard'. > On the other hand, the preprocessor allows to keep the more natural > syntax. There is a problem using the preprocessor hack; as-is the following code will deadlock: struct object : monitor { int m_state; // = 0 void foo() { when (m_state == 1) { // [...]; } when (m_state == 2) { // [...]; } } }; this is because the local objects created by the first call to `when' will still be around during the second call; the soultion is easy: struct object : monitor { int m_state; // = 0 void foo() { { when (m_state == 1) { // [...]; } } { when (m_state == 2) { // [...]; } } } }; but looks a bit awkward. Speaking of awkward, the version using pure C++ and no pre-processor would be kind of painful to code for. You would basically need to wrap up all the predicates in separate functions. Lets see here... It would be something like this: class when { monitor& m_monitor; public: template<typename P> when(monitor* monitor_, P pred) : m_monitor(*monitor_) { m_monitor.lock(); while (! pred()) m_monitor.wait(); } ~when() { m_monitor.broadcast(); m_monitor.unlock(); } }; template<typename T> struct buffer : monitor { unsigned const m_max; std::deque<T> m_buffer; bool pred_push() const { return m_buffer.size() < m_max; } bool pred_pop() const { return ! m_buffer.empty(); } public: buffer(unsigned const max_) : m_max(max_) {} void push(T const& obj) { when guard(this, pred_push); m_buffer.push_back(obj); } T pop() { when guard(this, pred_pop); T obj = m_buffer.front(); m_buffer.pop_front(); return obj; } }; This would work, but it forces you to create a special function per-predicate. Also, it suffers from the same problem the macro version does. Two when guard objects residing in the same scope will deadlock... Humm, personally, I kind of like the macro version better. It seems cleaner for some reason and allows one to use full expressions for the predicate instead of a special function. What would be neat is if I use an expression as a template parameter and have it be treated as if it were a function. The preprocessor makes this easy... |
|
|
|
|
|||
|
|||
| Chris M. Thomasson |
|
Chris M. Thomasson
Guest
Posts: n/a
|
[added: comp.lang.c++
here is link to Ulrich Eckhardt's full post because I snipped some of it: http://groups.google.com/group/comp....190e3b9ac81a69 ] "Ulrich Eckhardt" <> wrote in message news:... > Chris M. Thomasson wrote: > [C++ thread baseclass with virtual run() function] > > Just one thing technical about the code: your use reinterpret_cast in a > place that actually calls for a static_cast. A static_cast is the right > tool to undo the implicit conversion from T* to void*. > >> I personally like this technique better than Boost. I find it more >> straight forward and perhaps more object oriented, the RAII nature of the >> `active' helper class does not hurt either. Also, I really do think its >> more "efficient" than Boost in the way it creates threads because it does >> not copy anything... > > There are two things that strike me here: > 1. You mention "object oriented" as if that was a goal, but it isn't. > Rather, it is a means to achieve something, and the question is always > valid whether its use is justified. Java's decision to force an OO design > on you and then inviting other paradigms back in through the backdoor is > the prime example for misunderstood OO. Wrapping a thread into a class the > way you do it is another IMHO, ill explain below. > 2. What exactly is the problem with the copying? I mean you're starting a > thread, which isn't actually a cheap operation either. Further, if you > want, you can optimise that by using transfer of ownership (auto_ptr) or > shared ownership (shared_ptr) in case you need. Boost doesn't make it > necessary to copy anything either (except under the hood it does some > dynamic allocation), but also allows you to chose if you want. However, > copying and thus avoiding shared data is the safer default, because any > shared data access requires care. [...] If find it unfortunate to be forced to use an smart pointers and dynamically created objects just to be able to pass common shared data to a thread. I am still not convinced that treating a thread as an object is a bad thing... For instance... How would I be able to create the following fully compliable program (please refer to the section of code under the "Simple Example" heading, the rest is simple impl detail for pthread abstraction) using Boost threads. The easy way to find the example program is to go to the end of the entire program, and start moving up until you hit the "Simple Example" comment... Here it is: __________________________________________________ ___________________ /* Simple Thread Object __________________________________________________ ____________*/ #include <pthread.h> extern "C" void* thread_entry(void*); class thread_base { pthread_t m_tid; friend void* thread_entry(void*); virtual void on_active() = 0; public: virtual ~thread_base() = 0; void active_run() { pthread_create(&m_tid, NULL, thread_entry, this); } void active_join() { pthread_join(m_tid, NULL); } }; thread_base::~thread_base() {} void* thread_entry(void* state) { reinterpret_cast<thread_base*>(state)->on_active(); return 0; } template<typename T> struct active : public T { struct guard { T& m_object; guard(T& object) : m_object(object) { m_object.active_run(); } ~guard() { m_object.active_join(); } }; active() : T() { this->active_run(); } ~active() { this->active_join(); } template<typename T_p1> active(T_p1 p1) : T(p1) { this->active_run(); } template<typename T_p1, typename T_p2> active(T_p1 p1, T_p2 p2) : T(p1, p2) { this->active_run(); } template<typename T_p1, typename T_p2, typename T_p3> active(T_p1 p1, T_p2 p2, T_p3 p3) : T(p1, p2, p3) { this->active_run(); } // [and on and on for more params...] }; /* Simple Monitor __________________________________________________ ____________*/ class monitor { pthread_mutex_t m_mutex; pthread_cond_t m_cond; public: monitor() { pthread_mutex_init(&m_mutex, NULL); pthread_cond_init(&m_cond, NULL); } ~monitor() { pthread_cond_destroy(&m_cond); pthread_mutex_destroy(&m_mutex); } struct lock_guard { monitor& m_monitor; lock_guard(monitor& monitor_) : m_monitor(monitor_) { m_monitor.lock(); } ~lock_guard() { m_monitor.unlock(); } }; struct signal_guard { monitor& m_monitor; bool const m_broadcast; signal_guard(monitor& monitor_, bool broadcast = true) : m_monitor(monitor_), m_broadcast(broadcast) { } ~signal_guard() { if (m_broadcast) { m_monitor.broadcast(); } else { m_monitor.signal(); } } }; void lock() { pthread_mutex_lock(&m_mutex); } void unlock() { pthread_mutex_unlock(&m_mutex); } void wait() { pthread_cond_wait(&m_cond, &m_mutex); } void signal() { pthread_cond_signal(&m_cond); } void broadcast() { pthread_cond_broadcast(&m_cond); } }; #define when_xx(mp_pred, mp_line) \ monitor::lock_guard lock_guard_##mp_line(*this); \ monitor::signal_guard signal_guard_##mp_line(*this); \ while (! (mp_pred)) this->wait(); #define when_x(mp_pred, mp_line) when_xx(mp_pred, mp_line) #define when(mp_pred) when_x(mp_pred, __LINE__) /* Simple Example __________________________________________________ ____________*/ #include <string> #include <deque> #include <cstdio> template<typename T> struct bounded_buffer : monitor { unsigned const m_max; std::deque<T> m_buffer; public: bounded_buffer(unsigned const max_) : m_max(max_) {} void push(T const& obj) { when (m_buffer.size() < m_max) { m_buffer.push_back(obj); } } T pop() { when (! m_buffer.empty()) { T obj = m_buffer.front(); m_buffer.pop_front(); return obj; } } }; struct person : thread_base { typedef bounded_buffer<std::string> queue; std::string const m_name; queue& m_response; public: queue m_request; void on_active() { m_response.push(m_name + " is ready to receive some questions!"); for (unsigned i = 0 ;; ++i) { std::string msg(m_request.pop()); if (msg == "QUIT") { break; } std: switch (i) { case 0: msg = "(A)->" + m_name + ": Well, I am okay"; break; case 1: msg = "(A)->" + m_name + ": I already told you!"; break; default: msg = "(A)->" + m_name + ": I am ****ED OFF NOW!"; } m_response.push(msg); } std: m_response.push(m_name + " is FINISHED"); } person(std::string const& name, queue* q, unsigned const bound) : m_name(name), m_response(*q), m_request(bound) {} }; #define BOUND 10 int main(void) { { person::queue response(BOUND); active<person> chris("Chris", &response, BOUND); active<person> amy("Amy", &response, BOUND); std: std: chris.m_request.push("How are you doing?"); amy.m_request.push("How are you feeling?"); std: std: chris.m_request.push("Do you really feel that way?"); amy.m_request.push("Are you sure?"); std: std: chris.m_request.push("Why do you feel that way?"); amy.m_request.push("Can you share more of you feelings?"); std: std: chris.m_request.push("QUIT"); amy.m_request.push("QUIT"); std: std: } std: std::fflush(stdout); std::getchar(); return 0; } __________________________________________________ ___________________ Please correct me if I am wrong, but Boost would force me to dynamically create the `person::queue request' object in main right? AFAICT, this example shows why is can be a good idea to treat a thread as an object. In this case, a person object is a thread. Anyway, as of now, I am not entirely convinced that Boost has a far superior method of creating threads... Anyway, I really do need to think about the rest of your post; you raise several interesting issues indeed. |
|
|
|
|
|||
|
|||
| Chris M. Thomasson |
|
Chris M. Thomasson
Guest
Posts: n/a
|
"Chris M. Thomasson" <> wrote in message news:XR6Pk.3776$... [...] > "Ulrich Eckhardt" <> wrote in message > news:... >> Chris M. Thomasson wrote: [...]> > If find it unfortunate to be forced to use an smart pointers and > dynamically created objects just to be able to pass common shared data to > a thread. I am still not convinced that treating a thread as an object is > a bad thing... For instance... > > > How would I be able to create the following fully compliable program > (please refer to the section of code under the "Simple Example" heading, > the rest is simple impl detail for pthread abstraction) using Boost > threads. The easy way to find the example program is to go to the end of > the entire program, and start moving up until you hit the "Simple Example" > comment... Here it is: > __________________________________________________ ___________________ [...] > __________________________________________________ ___________________ > > > > > Please correct me if I am wrong, but Boost would force me to dynamically > create the `person::queue request' object in main right? AFAICT, this > example shows why is can be a good idea to treat a thread as an object. In > this case, a person object is a thread. Anyway, as of now, I am not > entirely convinced that Boost has a far superior method of creating > threads... [...] I should have made the characters `Chris' and `Amy' completely separate objects deriving from a `person' base-class . That way, their personalities and therefore their responses would not be identical. Treating threads as objects works very well for me perosnally... |
|
|
|
|
|||
|
|||
| Chris M. Thomasson |
|
Ulrich Eckhardt
Guest
Posts: n/a
|
Responding late, it was a rough week...
Chris M. Thomasson wrote: > "Ulrich Eckhardt" <> wrote in message > news:... >> Chris M. Thomasson wrote: >> [C++ thread baseclass with virtual run() function] >> >>> I personally like this technique better than Boost. I find it more >>> straight forward and perhaps more object oriented, the RAII nature of >>> the `active' helper class does not hurt either. Also, I really do think >>> its more "efficient" than Boost in the way it creates threads because it >>> does not copy anything... >> >> There are two things that strike me here: >> 1. You mention "object oriented" as if that was a goal, but it isn't. >> Rather, it is a means to achieve something, and the question is always >> valid whether its use is justified. Java's decision to force an OO design >> on you and then inviting other paradigms back in through the backdoor is >> the prime example for misunderstood OO. Wrapping a thread into a class >> the way you do it is another IMHO, ill explain below. >> 2. What exactly is the problem with the copying? I mean you're starting a >> thread, which isn't actually a cheap operation either. Further, if you >> want, you can optimise that by using transfer of ownership (auto_ptr) or >> shared ownership (shared_ptr) in case you need. Boost doesn't make it >> necessary to copy anything either (except under the hood it does some >> dynamic allocation), but also allows you to chose if you want. However, >> copying and thus avoiding shared data is the safer default, because any >> shared data access requires care. > > [...] > > If find it unfortunate to be forced to use an smart pointers and > dynamically created objects just to be able to pass common shared data to > a thread. I am still not convinced that treating a thread as an object is > a bad thing... For instance... > > > How would I be able to create the following fully compliable program > (please refer to the section of code under the "Simple Example" heading, > the rest is simple impl detail for pthread abstraction) using Boost > threads. The easy way to find the example program is to go to the end of > the entire program, and start moving up until you hit the "Simple Example" > comment... If I get your code right, this program is creating two threads, each of which models a person's behaviour. The main thread then asks them questions which are stored in a queue to be handled asynchronously and prints the answers which are received from another queue. Is that right? Now, how would I rather design this? Firstly, I would define the behaviour of the persons in a class. This class would be completely ignorant of any threading going on in the background and only model the behaviour. Then, I would create a type that combines a queue with a condition and a mutex. This could then be used to communicate between threads in a safe way. This would be pretty much the same as your version, both pthread_cond_t and pthread_mutex_t translate easily to boost::mutex and boost::condition. Now, things get a bit more complicated, because first some questions need to be answered. The first one is what the code should do in case of failures. What happens if one person object fails to answer a question? What if answering takes too long? What if the thread where the answers are generated is terminated due to some error? What if the invoking thread fails to queue a request, e.g. due to running out of memory? The second question to answer is what kind of communication you are actually modelling here. In the example, it seems as if you were making a request and then receiving the response to that request, but that isn't actually completely true. Rather, the code is sending a message and receiving a message, but there is no correlation between the sent and received message. If this correlation is required, I would actually return a cookie when I make a request and retrieve the answer via that cookie. In any case, you can write an equivalent program using Boost.Thread. If you want, you can wrap stuff into a class, e.g. like this: struct active_person { explicit active_person(queue& out_): out(out_), th( bind( &handle_requests, this)) {} ~active_person() { th.join(); } void push_question( std::string const& str) { in.push(str); } std::string pop_answer() { return out.pop(); } private: void handle_requests() { while(true) { std::string question = in.pop(); if(question=="QUIT") return; out.push(p.ask(question)); } } queue in; queue& out; person p; // note: this one must come last, because its initialisation // starts a thread using this object. boost::thread th; }; You could also write a simple function: void async_quiz( person& p, queue& questions, queue& answers) { while(true) { std::string question = questions.pop(); if(question=="QUIT") return; answers.push(p.ask(question)); } } queue questions, answers; person amy("amy"); thread th(bind( &async_quiz, ref(amy), ref(questions), ref(answers))); .... // ask questions th.join(); Note the use of ref() to avoid the default copying of the argument. Using pointers would work, too, but I find it ugly. In any case, this is probably not something I would write that way. The point is that I can not force a thread to shut down or handle a request. I can ask it to and maybe wait for it (possibly infinitely), but I can not force it. So, if there is any chance for failure, both on the grounds of request handling or the request handling mechanism in general, this approach becomes unusable. Therefore, I rather prepare for the case that a thread becomes unresponsive by allowing it to run detached from the local stack. Writing about that, there is one thing that came somehow as a revelation to me, and that was the Erlang programming language. It has concurrency built in and its threads (called processes) communicate using messages. Using a similar approach in C++ allows writing very clean programs that don't suffer lock contention. In any case, I suggest taking a look at Erlang just for the paradigms, I found some really good ideas to steal from there. > #define when_xx(mp_pred, mp_line) \ > monitor::lock_guard lock_guard_##mp_line(*this); \ > monitor::signal_guard signal_guard_##mp_line(*this); \ > while (! (mp_pred)) this->wait(); > > #define when_x(mp_pred, mp_line) when_xx(mp_pred, mp_line) > #define when(mp_pred) when_x(mp_pred, __LINE__) Just one note on this one: When I read your example, I stumbled over the use of a 'when' keyword where I would expect an 'if'. I find this here really bad C++ for several reasons: 1. Macros should be UPPERCASE_ONLY. That way, people see that it's a macro and they know that it may or may not behave like a function. It simply avoids surprises. 2. It is used in a way that breaks its integration into the control flow syntax. Just imagine that I would use it in the context of an 'if' expression: if(something) when(some_condition) do_something_else(); I'd rather write it like this: if(something) { LOCK_AND_WAIT_CONDITION(some_condition); do_something_else(); } Firstly, you see that these are separate statements and this then automatically leads to you adding the necessary curly braces. > Please correct me if I am wrong, but Boost would force me to dynamically > create the `person::queue request' object in main right? No. The argument when starting a thread is something that can be called, like a function or functor, that's all. This thing is then copied before it is used by the newly started thread. Typically, this thing is a function bound to its context arguments using Boost.Bind or Lambda. If you want, you can make this a simple structure containing a few pointers or references, i.e. something dead easy to copy. cheers Uli |
|
|
|
|
|||
|
|||
| Ulrich Eckhardt |
|
Szabolcs Ferenczi
Guest
Posts: n/a
|
On Nov 6, 11:24*pm, Ulrich Eckhardt <dooms...@knuut.de> wrote:
> Responding late, it was a rough week... > > Chris M. Thomasson wrote: > > "Ulrich Eckhardt" <dooms...@knuut.de> wrote in message > >news:... > >> Chris M. Thomasson wrote: [...] > > #define when_xx(mp_pred, mp_line) \ > > * monitor::lock_guard lock_guard_##mp_line(*this); \ > > * monitor::signal_guard signal_guard_##mp_line(*this); \ > > * while (! (mp_pred)) this->wait(); > > > #define when_x(mp_pred, mp_line) when_xx(mp_pred, mp_line) > > #define when(mp_pred) when_x(mp_pred, __LINE__) > > Just one note on this one: When I read your example, I stumbled over the use > of a 'when' keyword where I would expect an 'if'. The `when' is basically different from the `if'. Since I have come up with this construction, I might give you an answer. The `when' is not a genuine keyword since it is still C++ code and the `when' is not a C++ keyword. However, here we are experimenting whether a programming style could be applied in a C++ program that mimics the original Conditional Critical Region programming proposal where the `when' is a keyword. The `if' is a sequential control statement whereas the `when' is not one. The semantics of the `if' and the `when' differs in that the `if' fails when the condition does not hold at the time it is evaluated and the statement takes the `else' branch if it is given. The `when' keyword, on the other hand, delays the thread until the condition is not true. In other words, the `when' specifies a guard. > I find this here really > bad C++ for several reasons: > 1. Macros should be UPPERCASE_ONLY. That way, people see that it's a macro > and they know that it may or may not behave like a function. It simply > avoids surprises. It is not necessarily meant to be a macro, the macro is just one possible implementation of the construction. > 2. It is used in a way that breaks its integration into the control flow > syntax. Just imagine that I would use it in the context of an 'if' > expression: > > * if(something) > * * when(some_condition) > * * * do_something_else(); > > I'd rather write it like this: > > * if(something) { > * * LOCK_AND_WAIT_CONDITION(some_condition); > * * do_something_else(); > * } > > Firstly, you see that these are separate statements and this then > automatically leads to you adding the necessary curly braces. Please check out the post where the `when' construction is introduced into this discussion thread: http://groups.google.com/group/comp....476c1c7d91c008 If you have an idea how to solve the original problem I described, your solution is welcome. Best Regards, Szabolcs |
|
|
|
|
|||
|
|||
| Szabolcs Ferenczi |
|
Chris M. Thomasson
Guest
Posts: n/a
|
"Ulrich Eckhardt" <> wrote in message news:... > Responding late, it was a rough week... [...] > In any case, you can write an equivalent program using Boost.Thread. If > you > want, you can wrap stuff into a class, e.g. like this: > > struct active_person > { > explicit active_person(queue& out_): > out(out_), > th( bind( &handle_requests, this)) > {} > ~active_person() > { th.join(); } > void push_question( std::string const& str) > { in.push(str); } > std::string pop_answer() > { return out.pop(); } > private: > void handle_requests() > { > while(true) > { > std::string question = in.pop(); > if(question=="QUIT") > return; > out.push(p.ask(question)); > } out.push("QUIT"); > } > queue in; > queue& out; > person p; > // note: this one must come last, because its initialisation > // starts a thread using this object. > boost::thread th; > }; [...] I would create the active template using Boost like: // quick sketch - may have typo __________________________________________________ _____________________ template<typename T> class active : public T { boost::thread m_active_handle; public: active() : T(), m_active_handle(bind(&on_active, (T*)this)) {} ~active() { m_active_handle.join(); } template<typename P1> active(P1 p1) : T(p1), m_active_handle(bind(&on_active, (T*)this)) {} template<typename P1, typename P2> active() : T(P1, P2), m_active_handle(bind(&on_active, (T*)this)) {} // [on and on for more params...] }; __________________________________________________ _____________________ Then I could use it like: struct person { void on_active() { // [...] } }; int main() { active<person> p[10]; return 0; } I personally like this construction; I think its "cleaner" than using the Boost interface "directly". |
|
|
|
|
|||
|
|||
| Chris M. Thomasson |
|
|
|
| |
![]() |
| Thread Tools | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| looking for elegant C++ abstraction around pthread_key_t... | Chris M. Thomasson | C++ | 7 | 10-31-2008 10:16 PM |
| Microsoft & patents: what goes around comes around... | Lawrence D'Oliveiro | NZ Computing | 104 | 12-16-2006 07:11 AM |
| How do I parse a string into individual characters? (really simple!) really! | Jeannie | C++ | 15 | 08-30-2005 08:34 AM |
| Read all of this to understand how it works. then check around on otherRead all of this to understand how it works. then check around on other | thelisa martin | Computer Support | 2 | 08-18-2005 06:40 AM |
| Make wxListCtrl fit around contents and parent frame fit around listctrl | Piet | Python | 0 | 07-18-2004 08:27 AM |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc..
SEO by vBSEO ©2010, Crawlability, Inc. |




