*From:* boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] *On Behalf Of *Steve Lorimer *Sent:* Wednesday, June 02, 2010 4:36 PM *To:* boost-users@lists.boost.org *Subject:* Re: [Boost-users] [bind] & [function] How do I store member functions with generalised arguments for later invocation?
From a quick perusal (almost bed time :) !) I can't see how I would achieve my aim, which is:
1. create the "event" (signal) which is associated with a member function "callback" (slot). Associate a thread with the event
(can I create a signal connected to a slot which is a member function?)
Yes, you can either use boost::bind() explicitly (or the Boost Lambda Library or Boost Phoenix), or I believe boost::signals2::signal::connect() will accept bind-like arguments and call boost::bind() internally.
2. in any other thread, call "post" (sig(data)) where data is a shared_ptr
3. the callback (slot) is called, running in the context of the *other* thread (the one originally associated with the "event" / (slot).
I'm trying to encapsulate a generic inter-thread messaging system where objects are passed around between threads, and associated with a member function which is designed to process said objects.
The producer creates an "event" object which is associated with the consumer_thread object, it's void consume(struct data) member function, and the thread which must process the data (consumer_thread). Now when the producer produces some data, it calls event->post(data), which queues up an object which records the class instance and it's member function which must be called with parameter "data", and a shared_ptr to data. The consumer thread wakes up, gets the top of the queue, and calls obj->mem_fun(data) --> ergo, mem_fun is called in the correct thread context, and has the argument "data" to consume.
John Dlugosz wrote:
I’m thinking of the same thing, and would reach for std::tr1::function generalized function objects to be the callback object queued between threads.
I don't quite understand your requirements, but let me take a shot. The important point that John brings out is that if you want the consumer to run code on its own thread, rather than running on the producer's thread, then I think we're talking about a queue. This would be instead of using Boost.Signals2 at all, which would call its slots immediately on the producer's thread. So you instantiate a queue and start the consumer thread waiting for items to arrive on that queue. I agree with John that it sounds like the "items" in question are std::tr1::function objects (or I'd say boost::function objects, but whatever). When you say:
The consumer thread wakes up, gets the top of the queue, and calls obj->mem_fun(data)
it's not really clear to me whether 'obj' is owned by the consumer thread or is in the hands of the producer object. Let's consider both cases. ==== Either way: class Data { ... }; typedef boost::shared_ptr<Data> DataPtr; ==== If 'obj' is the ConsumerClass instance: class ConsumerClass { public: ... void run(); void doSomething(DataPtr data); void somethingElse(DataPtr data); ... }; typedef (ConsumerClass::*ConsumerMethod)(DataPtr); typedef boost::function<void(ConsumerClass*)> QueueItem; typedef YourFavoriteThreadSafeQueue<QueueItem> Queue; void ConsumerClass::run() { for (;;) { QueueItem item(queue.get()); item(this); } } void ProducerClass::post(ConsumerMethod method, DataPtr data) { queue.put(boost::bind(method, _1, data)); } ==== If 'obj' is owned instead by the producer thread: class ConsumerClass { public: ... void run(); ... }; class ProcessorClass { public: ... void doSomething(DataPtr data); void somethingElse(DataPtr data); ... }; typedef (ProcessorClass::*ProcessorMethod)(DataPtr); typedef boost::function<void()> QueueItem; typedef YourFavoriteThreadSafeQueue<QueueItem> Queue; void ConsumerClass::run() { for (;;) { QueueItem item(queue.get()); item(); } } void ProducerClass::post(ProcessorClass& obj, ProcessorMethod method, DataPtr data) { queue.put(boost::bind(method, obj, data)); } ==== If you have a number of consumer threads, and you want the producer to select among them, I'd associate a separate queue with each thread and have the producer put items to the appropriate queue, in effect implying the consumer thread by the producer's choice of queue. I'd better stop here and let you explain what parts I've badly misunderstood.