Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Getting the address of a member function

2 views
Skip to first unread message

Paul Davis

unread,
May 21, 2003, 7:58:37 AM5/21/03
to
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.

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 Williams

unread,
May 21, 2003, 8:25:27 AM5/21/03
to
Have you tried a function object? The function object could be
contructed with the current foo instance and its () operator would call
the "bar" function binded to the foo object used at contruction.
Haevn't tried it with pthread_create, but it might work.

-Robb

tom_usenet

unread,
May 21, 2003, 8:52:45 AM5/21/03
to
On Wed, 21 May 2003 12:58:37 +0100, Paul Davis
<mdhe50ATdialDOTpipexDOTcom> wrote:

>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

Paul Davis

unread,
May 21, 2003, 9:43:30 AM5/21/03
to
On Wed, 21 May 2003 12:52:45 GMT, tom_u...@hotmail.com (tom_usenet)
wrote:

>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

Rolf Magnus

unread,
May 21, 2003, 9:56:03 AM5/21/03
to
tom_usenet wrote:

> 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?

Victor Bazarov

unread,
May 21, 2003, 10:18:40 AM5/21/03
to
"Rolf Magnus" <rama...@t-online.de> wrote...


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


Alexander Terekhov

unread,
May 21, 2003, 10:38:41 AM5/21/03
to

Victor Bazarov wrote:
>
> "Rolf Magnus" <rama...@t-online.de> wrote...
> > tom_usenet wrote:
> >
> > > 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.

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.

Luther Baker

unread,
May 21, 2003, 10:57:24 PM5/21/03
to

OT but - a VC++ WNDPROC may also use a static member function.

-Luther

tom_usenet

unread,
May 22, 2003, 5:52:58 AM5/22/03
to
On Wed, 21 May 2003 15:56:03 +0200, Rolf Magnus <rama...@t-online.de>
wrote:

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

Alexander Terekhov

unread,
May 23, 2003, 8:21:40 AM5/23/03
to

tom_usenet wrote:
>
> On Wed, 21 May 2003 15:56:03 +0200, Rolf Magnus <rama...@t-online.de>
> wrote:
>
> >tom_usenet wrote:
> >
> >> 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?
>
> This is also a problem using the template approach (a template can't
> be extern "C").

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.

0 new messages