Boost logo

Boost Users :

Subject: Re: [Boost-users] [bind] & [function] How do I store member functions with generalised arguments for later invocation?
From: Nat Goodspeed (nat_at_[hidden])
Date: 2010-06-03 17:11:48


> *From:* boost-users-bounces_at_[hidden]
> [mailto:boost-users-bounces_at_[hidden]] *On Behalf Of *Steve Lorimer
> *Sent:* Wednesday, June 02, 2010 4:36 PM
> *To:* boost-users_at_[hidden]
> *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.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net