passing parameter lists to a thread

Started by
11 comments, last by Zahlman 15 years, 10 months ago
Is there a better way than this:
spawn a thread()
{
	...
	std::vector<void*> *params = new std::vector<void*>;
	params->push_back(object);
	params->push_back(name);

	Thread *t = new Thread(EntryPoint, params);
}
and put the thread pointer in some kind of smart pointer to lose the memory leak. fwiw this implementation crashes on some computers but not all with apparent memory corruption.
Advertisement
Quote:Original post by thedustbustr
Is there a better way than this:


Yes, boost::thread + boost::bind

Could you post some actual code, its hard to tell what is wrong with your current implementation.

You can't use a void* variable to store general objects. You can use it to point at general objects. But it's still incredibly bad mojo. How are you going to figure out what kind of thing is pointed-at on the receiving end?

Why are you passing trying to pass "general" parameters to your Thread object? Are you planning for the user to derive Thread and expect a specific set of parameters? In that case, consider making it a template, and offering just one constructor parameter of type T. The user can "pass multiple parameters" by letting T be a struct that contains what it needs to.
I tend to use boost::variant when there's a possibility of a few different types of things being passed through. You can use boost::any, but boost::any code winds up being a bit of an annoyance, since now "anything" can be passed through, which leads to client code which has to handle *everything* ==> brutal. But, definitely boost::variant<> instead of void* should get you through this, as long as you can limit the sorts of things you might want to pass through.
I can't use boost.thread; my procs must conform to
typedef void (*procType)(const Thread *, void *);
I can do whatever I want in my entry point so long as it is declared as such.

Here's what I've got originally which is obviously horrible:

void Foo::EntryPoint(const Thread* thisThread, void* data){	UnpackParameters(data);...}void Foo::UnpackParameters(void *data){	std::vector<void*> *params = reinterpret_cast<  std::vector<void*> *  >(data);	void *first = (*params)[0];	frame = reinterpret_cast<RawDynamicPanel*>(first);	void *second = (*params)[1];	command = reinterpret_cast<char*>(second);}


So, instead, I'll have the param always be a std::list of boost::any? I don't see any functionality difference over void* other than better error checking.

Is this what you meant by boost bind? would this work? I like this.
Thread *t = new Thread(boost::bind(Foo::EntryPoint, object, name), (void*)NULL);
Quote:I can't use boost.thread; my procs must conform to

typedef void (*procType)(const Thread *, void *);


Is there a genunite constraint here - for example are you dealing with a 3rd party api or library, or else a huge slice of your own code?

If this is not the case then do consider boost::bind and boost::threading as a relatively standard c++ way of doing this stuff.

bind works a bit differently to normal functions - the cleverness is that it enables you to create a new function from an existing function, but with the existing functions parameters pre-bound with argument values.

This is a lot more flexible and type safe, as well as being much less work than trying to hand marshall, by packing and unpacking your arguments around the entry interface function. effectively what it buys you, is the ability to start a new thread and call whatever entry function/method you wish, with whatever arguments you want from the controlling thread context, while having the execution of the function run in the newly created thread context.

an example ...
#include <iostream>#include <string>#include <boost/thread.hpp>#include <boost/bind.hpp>//#include <boost/thread/mutex.hpp>//#include <boost/thread/condition.hpp>using namespace std;struct A{    void do_something_sexy( const string &what)    {           cout << "i will " << what << endl;    }};struct B{    void report_game_stats( int match, float score1, float score2 )    {           cout << "match " << match << " " << score1 << " " << score2 << endl;    }};int main(){using boost::thread;using boost::bind;    A   a;    B   b;    // create 2 new threads    thread      t0( bind( &A::do_something_sexy, a, "dance"));    thread      t1( bind( &B::report_game_stats, b, 3, 77.7f, 66.3));    t0.join();    t1.join();    return 0;}
Quote:Original post by thedustbustr
I can't use boost.thread; my procs must conform to
typedef void (*procType)(const Thread *, void *);
I can do whatever I want in my entry point so long as it is declared as such.

...

Is this what you meant by boost bind? would this work? I like this.
Thread *t = new Thread(boost::bind(Foo::EntryPoint, object, name), (void*)NULL);


Yes that is what I meant (almost). Since you are using a member function you also need to to bind the this pointer as the first argument to your function. This is because all member functions implicitly accept this as their first, albeit hidden, argument. After all, it needs to know what instance to call the function on.

One of the many advantages of boost::thread is that it allows you to bind a function with any arbitrary signature to a thread.
error C2276: '&' : illegal operation on bound member function expressionclass Foo {boost::thread *ioproc;void IOProc(std::string remote_hostname, std::string remote_port);setup() {ioproc = new boost::thread(boost::bind(&IOProc, this, "localhost", "65123"));}~Foo() {delete ioproc;}};
Quote:Original post by thedustbustr
I can't use boost.thread; my procs must conform to
typedef void (*procType)(const Thread *, void *);
I can do whatever I want in my entry point so long as it is declared as such.

Here's what I've got originally which is obviously horrible:

*** Source Snippet Removed ***

So, instead, I'll have the param always be a std::list of boost::any? I don't see any functionality difference over void* other than better error checking.

Is this what you meant by boost bind? would this work? I like this.
Thread *t = new Thread(boost::bind(Foo::EntryPoint, object, name), (void*)NULL);


1) That doesn't work if Foo is a class and EntryPoint is a non-static member function. Pointers to member functions cannot be treated as pointers to ordinary functions. Hint: Which object do you want it called upon?

2) Foo::EntryPoint knows what type of data it needs, exactly. The normal way to use void* here is to make a structure that represents Foo::EntryPoint's parameters, and pass an instance via the void*. Then, each other possible "thread callback" does similarly for its parameters.

struct FooParams{	// Don't use unsafe things in your structure without a reason :)	boost::shared_ptr<RawDynamicPanel> frame;	std::string command;	FooParams(boost::shared_ptr<RawDynamicPanel> frame, const std::string& command): frame(frame), command(command) {}};void Foo::EntryPoint(const Thread* thisThread, void* data){	FooParams* fp = reinterpret_cast<FooParams*>(data);	DoWorkWith(fp->frame, fp->command);...}// And then later, something like:ThreadManager::launch(Foo::EntryPoint, FooParams(myFrame, myCommand));
Quote:Original post by Zahlman
1) That doesn't work if Foo is a class and EntryPoint is a non-static member function. Pointers to member functions cannot be treated as pointers to ordinary functions. Hint: Which object do you want it called upon?


Sure he can. Thats what all that boost::thread and boost::bind magic is all about.

This should work (I'd need to double check the semantics for binding the last two arguments):
void setup() {ioproc = new boost::thread(boost::bind(&Foo::IOProc, this, "localhost", "65123"));}


Quote:Original post by Zahlman
Hint: Which object do you want it called upon?


We bind the implicit first argument, the this pointer.

These APIs are lovely, no? :)

This topic is closed to new replies.

Advertisement