The background is that I have a member function - say foo::bar - that
I want to start as an independent thread, using the Posix threads
'pthread_create' call. This routine has to be given the address of
'bar'. As a complication, I don't know in advance how many 'foo'
classes are required. They're created at runtime, and I need to start
a new thread for every instance of foo::bar.
I understand why I can't take the address of a member function, and
the simple fix is just to make 'bar' an 'ordinary' function. However,
the problem is now that I need a variable number of bar's, and I can't
create 'ordinary' functions.
Does anyone have any ideas on work-arounds? I'm using GCC which does
have an extension which allows you to take the address of a member
function, but I can't get it to work, and I'd rather not use it if
possible.
Many thanks
Paul
-Robb
>Hi - any help on this greatly appreciated. I'm looking for some
>work-around to get the address of a member function.
>
>The background is that I have a member function - say foo::bar - that
>I want to start as an independent thread, using the Posix threads
>'pthread_create' call. This routine has to be given the address of
>'bar'. As a complication, I don't know in advance how many 'foo'
>classes are required. They're created at runtime, and I need to start
>a new thread for every instance of foo::bar.
There is only one version of foo::bar - all instances of a class share
the member functions. The implicit "this" pointer is passed as a
hidden argument to member function calls.
myfoo->bar();
is translated to something like:
foo_bar(myfoo);
>
>I understand why I can't take the address of a member function, and
>the simple fix is just to make 'bar' an 'ordinary' function. However,
>the problem is now that I need a variable number of bar's, and I can't
>create 'ordinary' functions.
You mean you need a variable number of foo's. There is only one bar,
but you have multiple foo objects.
>Does anyone have any ideas on work-arounds? I'm using GCC which does
>have an extension which allows you to take the address of a member
>function, but I can't get it to work, and I'd rather not use it if
>possible.
The usual approach is to wrap the member function with a static member
function (all this assumes pthread_create requires a function with the
signature void (void*))
class foo
{
//...
void bar();
static void bar_wrapper(void* param);
};
void foo::bar_wrapper(void* param)
{
foo* p = static_cast<foo*>(param);
p->bar();
}
And then create the thread with:
create(&foo::bar_wrapper, myfoo);
passing the particular foo object you want to use.
Alternatively, to save you writing the static member function each
time, you might be able to use a template function, like this:
template <class T, void (T::*FP)()>
void member_forwarder(void* param)
{
T* p = static_cast<T*>(param);
(p->*FP)();
}
and
create(&member_forwarder<foo, &foo::bar>, myfoo);
Tom
>The usual approach is to wrap the member function with a static member
>function (all this assumes pthread_create requires a function with the
>signature void (void*))
Thanks - this is nice and simple and seems to have done the job (ie.
I've now moved on to the next bug :)).
Cheers
Paul
> The usual approach is to wrap the member function with a static member
> function (all this assumes pthread_create requires a function with the
> signature void (void*))
Wouldn't it need to be a non-member function declared as extern "C",
since pthread_create is expecting a pointer to a C function?
It is language-specific and generally unspecified, but two linkages
(C++ and C) _could_ be compatible to the extent when a function with
C++ linkage can be used there. 'pthread_create' is non-standard, so
there is nothing that would prevent it from allowing that, IMHO.
Victor
--
Please remove capital A's from my address when replying by mail
pthread_create() is a pretty standard POSIX.1 function. It has the C
linkage and expects the same linkage for the routine referenced by
its 'start_routine' function pointer argument. Only the 'upcomming'
<cthread> header can provide a *standard* extern "C++" 'overload' for
the pthread_create() and other similiar <pthread.h> stuff. BTW, std::
name-spacing aside, that's exactly what <cstdlib> does for bsearch()
and qsort() functions.
http://groups.google.com/groups?selm=3EB7D583.66C28170%40web.de
(Subject: Re: Newbie: thread function in class)
http://groups.google.com/groups?selm=3EC3AA39.7126DCCC%40web.de
(Subject: Re: Double-checked locking and memory barriers)
regards,
alexander.
OT but - a VC++ WNDPROC may also use a static member function.
-Luther
This is also a problem using the template approach (a template can't
be extern "C"). However, if your compiler requires and enforces that
pthread_create take an extern "C" function, it will tell you so with a
compiler error. Certainly GCC isn't fussy on the platforms I've tried
it on - calling C++ functions through function pointers is generally
identical to calling C ones.
Is there a platform where this isn't the case?
Tom
Yeah, <http://google.com/groups?selm=3ECCAAEE.3ACDB4D0%40web.de> (I
mean "{ugly-ugly-ugly} linkage problem"). However,
#include <pthread.h>
#include <new> // for std::bad_alloc, std::try_again aside for a moment
#include <cassert>
struct no_cleanup {
void operator()(void *) {
// noop
}
};
template< typename T >
struct deleter {
void operator()(T * p) {
delete p;
}
};
extern "C" typedef void (* c_TSD_dtor_t)(void *);
template< typename T >
c_TSD_dtor_t c_TSD_dtor();
template<>
c_TSD_dtor_t c_TSD_dtor< no_cleanup >() {
return 0;
}
extern "C" void c_deleter_for_int_ptr(void * p) {
delete static_cast<int *>(p);
}
template<>
c_TSD_dtor_t c_TSD_dtor< deleter< int > >() {
return c_deleter_for_int_ptr;
}
/* ... other {needed} "c_deleter"-stuff ... */
template< typename T, typename cleanup >
class thread_specific_ptr /* noncopyable [for now] */ {
pthread_key_t m_key;
public:
thread_specific_ptr() throw(std::bad_alloc/*, std::try_again*/) {
int status = pthread_key_create(&m_key, c_TSD_dtor< cleanup >());
// throw std::bad_alloc if status == ENOMEM
// throw std::try_again if status == EAGAIN
assert(!status);
}
~thread_specific_ptr() throw() {
int status = pthread_key_delete(m_key);
assert(!status);
}
T * get() const throw() {
return static_cast<T *>(pthread_getspecific(m_key));
}
void set(T * p) const throw(std::bad_alloc) {
int status = pthread_setspecific(m_key, p);
// throw std::bad_alloc if status == ENOMEM
assert(!status);
}
T * operator->() const throw() {
return get();
}
T & operator*() const throw() {
return *get();
}
T * release() throw() {
T * p = get();
if (p) set(0); // only an idiot will throw std::bad_alloc here
return p;
}
void reset(T * p /* "probably" != 0 */) throw(std::bad_alloc) {
T * old_p = get();
if (old_p != p) {
set(p);
cleanup()(old_p);
}
}
void reset() throw() {
cleanup()(release());
}
}; // <ES-link>
Oder?
regards,
alexander.