Pointer to member function

Discussion in 'C++' started by Dan Smithers, Jun 26, 2008.

  1. Dan Smithers

    Dan Smithers Guest

    I want to implement a C++ wrapper to C code that requires a function
    pointer to be passed in. Specifically, I want a wrapper for pthread that
    clients can use as a base class.

    Is there a way of passing a non-static member function in that will do
    this?

    In the following code, if I use the static k_main then it builds and
    runs, but the derived class uses the implementation in CMyThread.

    If I use a virtual member function, I get a compile error:
    thread2.cpp:21: error: argument of type ‘void* (CMyThread::)(void*)’
    does not match ‘void* (*)(void*)’

    Presumably this is because the member function pointer still expect the
    this parameter when it is called.

    I have thought of an alternative way that requires the CMyThread
    constructor to pass the this pointer as an additional argument that is
    then used in k_main to call the member function. It just looks really
    horrible.


    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.h>

    class CMyThread
    {
    protected:
    pthread_t m_thread;
    void *m_args;

    static void *k_main( void *ptr );
    virtual void *m_main( void *ptr );
    public:
    CMyThread(char *name);
    virtual ~CMyThread();
    };

    CMyThread::CMyThread(char *args)
    : m_args(args)
    {
    int rc = pthread_create( &m_thread, NULL, m_main, (void *)args);
    }

    CMyThread::~CMyThread()
    {
    pthread_join( m_thread, NULL);
    }

    void *CMyThread::k_main(void *args)
    {
    char *message;
    message = (char *) args;
    printf("Base Static %s \n", message);
    }

    void *CMyThread::m_main(void *args)
    {
    char *message;
    message = (char *) args;
    printf("Base Virtual %s \n", message);
    }

    class CMyDerivedThread : public CMyThread
    {
    protected:
    static void *k_main( void *ptr );
    virtual void *m_main( void *ptr );

    public:
    CMyDerivedThread(char *name);
    virtual ~CMyDerivedThread();
    };

    CMyDerivedThread::CMyDerivedThread(char *args)
    : CMyThread(args)
    {
    }

    CMyDerivedThread::~CMyDerivedThread()
    {
    }

    void *CMyDerivedThread::k_main(void *args)
    {
    char *message;
    message = (char *) args;
    printf("Derived Static %s \n", message);
    }

    void *CMyDerivedThread::m_main(void *args)
    {
    char *message;
    message = (char *) args;
    printf("Derived Virtual %s \n", message);
    }

    main()
    {
    pthread_t thread1, thread2;
    char *message1 = "Thread 1";
    char *message2 = "Thread 2";

    CMyThread t1(message1);

    CMyThread t2(message2);

    CMyDerivedThread("Thread 3");

    exit(0);
    }
     
    Dan Smithers, Jun 26, 2008
    #1
    1. Advertising

  2. On Jun 26, 3:12 pm, Dan Smithers <> wrote:
    > I want to implement a C++ wrapper to C code that requires a function
    > pointer to be passed in. Specifically, I want a wrapper for pthread that
    > clients can use as a base class.
    >
    > Is there a way of passing a non-static member function in that will do
    > this?
    >


    See http://www.parashift.com/c -faq-lite/pointers-to-members.html
     
    tragomaskhalos, Jun 26, 2008
    #2
    1. Advertising

  3. Dan Smithers

    kamit Guest

    Check out boost::thread.

    On Jun 26, 12:17 pm, tragomaskhalos <>
    wrote:
    > On Jun 26, 3:12 pm, Dan Smithers <> wrote:
    >
    > > I want to implement a C++ wrapper to C code that requires a function
    > > pointer to be passed in. Specifically, I want a wrapper for pthread that
    > > clients can use as a base class.

    >
    > > Is there a way of passing a non-static member function in that will do
    > > this?

    >
    > Seehttp://www.parashift.com/c++-faq-lite/pointers-to-members.html
     
    kamit, Jun 26, 2008
    #3
  4. Dan Smithers

    Dan Smithers Guest

    kamit wrote:
    > Check out boost::thread.
    >
    > On Jun 26, 12:17 pm, tragomaskhalos <>
    > wrote:
    >> On Jun 26, 3:12 pm, Dan Smithers <> wrote:
    >>
    >>> I want to implement a C++ wrapper to C code that requires a function
    >>> pointer to be passed in. Specifically, I want a wrapper for pthread that
    >>> clients can use as a base class.
    >>> Is there a way of passing a non-static member function in that will do
    >>> this?

    >> Seehttp://www.parashift.com/c++-faq-lite/pointers-to-members.html

    >


    Thanks, I've bookmarked the FAQ now (until Firefox eats the list).
    I've done it by passing a this to a static wrapper.#

    dan
     
    Dan Smithers, Jun 27, 2008
    #4
  5. Dan Smithers

    James Kanze Guest

    On Jun 26, 4:12 pm, Dan Smithers <> wrote:
    > I want to implement a C++ wrapper to C code that requires a
    > function pointer to be passed in. Specifically, I want a
    > wrapper for pthread that clients can use as a base class.


    > Is there a way of passing a non-static member function in that
    > will do this?


    No. There's not even a way of passing a static member function.

    First, of course, the calling conventions, and even the calling
    syntax, of a non-static member function is necessarily different
    from that of a static member or a free function; when calling a
    non-static member, the compiler must associate an object with
    the call. Obviously, there's no way that a C function can do
    this. But there's also no guarantee that C and C++ use the same
    calling conventions (and I've used compilers where they didn't).
    And since the ``extern "C"'' function is going to call the
    function whose address you pass it using the C calling
    conventions, you can only pass it a pointer to a function which
    uses the C calling conventions: a function declared ``extern
    "C"'' in C.

    In the case of functions like pthread_create, which also take a
    void*, be very careful about your conversions to and from void*
    as well. Once you've got the void*, the only thing you can do
    with it is cast it back to the exact type which was converted to
    get it. In particular, if you call pthread_create with a
    Derived* as the last argument, then in the start up function,
    you must cast it back to Derived*; casting it to Base* results
    in undefined behavior.

    > In the following code, if I use the static k_main then it
    > builds and runs, but the derived class uses the implementation
    > in CMyThread.


    > If I use a virtual member function, I get a compile error:
    > thread2.cpp:21: error: argument of type ?void*
    > (CMyThread::)(void*)? does not match ?void* (*)(void*)?


    > Presumably this is because the member function pointer still
    > expect the this parameter when it is called.


    Member function pointers typically have a completely different
    layout from non-member function pointers, precisely because they
    must be able to handle virtual functions. (If you take the
    address of a virtual &Base::f, and call it through a Derived* p,
    (p->*f)(), or through a Base* which actually points to a
    Derived, it is Derived::f() which will be called.)

    > I have thought of an alternative way that requires the
    > CMyThread constructor to pass the this pointer as an
    > additional argument that is then used in k_main to call the
    > member function. It just looks really horrible.


    It's the standard procedure. It is, in fact, the exact reason
    why the void* is there in pthread_create (and most other
    functions of its ilk). Calling a member function requires a
    user supplied object. The purpose of the void* is to allow the
    user to pass a pointer to an "object" (which, of course, can be
    anything the user wants; boost::thread passes the address of a
    Boost functional object, for example, and in some very special
    cases, I've used it to pass a small integer, with some pretty
    hairy reinterpret_casting).

    > #include <stdio.h>
    > #include <stdlib.h>
    > #include <pthread.h>


    > class CMyThread


    (Just an aside, but a single capital C prefix is the convention
    for MFC classes. I'd avoid it unless I was actually
    implementing MFC.)

    > {
    > protected:
    > pthread_t m_thread;
    > void *m_args;


    > static void *k_main( void *ptr );
    > virtual void *m_main( void *ptr );
    > public:
    > CMyThread(char *name);
    > virtual ~CMyThread();
    > };


    > CMyThread::CMyThread(char *args)
    > : m_args(args)
    > {
    > int rc = pthread_create( &m_thread, NULL, m_main, (void *)args);


    If this compiles, your compiler is broken. And replacing m_main
    with k_main shouldn't change anything here. You need a
    separate free function:

    extern "C" void*
    threadStarter( void* args )
    {
    static_cast< CMyThread* >( args )->m_main() ;
    }

    And since you're dealing with an object, you don't need the
    additional args---they can just be member variables of the
    object.

    Finally, of course, you can't call pthread_create from a
    constructor of a base class without encuring race conditions; as
    a general rule, you should never call it from the constructor of
    a "thread" object which you expect run.

    > }


    > CMyThread::~CMyThread()
    > {
    > pthread_join( m_thread, NULL);


    pthread_join can block. I'm not sure its a good idea to call it
    from a destructor. (Destructors are called during stack
    walkback, in case of an exception. Which is generally a context
    where you don't want to block for an indefinite time.)

    > }


    The usual solution for this sort of thing is to separate the
    thread from what it does. Not only does it work, but it's a lot
    cleaner in the long run. Thus, you might end up with something
    like:

    class Executable
    {
    public:
    virtual ~Executable() {}
    virtual void run() = 0 ;

    private:
    Executable( Executable const& ) ;
    Executable& operator=( Executable const& ) ;
    };

    class Thread
    {
    public:
    explicit Thread( Executable* exec ) ;
    ~Thread() ;
    Executable* join() ;

    private:
    Thread( Thread const& ) ;
    Thread& operator=( Thread const& ) ;

    private:
    Executable* myExecutable ;
    pthread_t threadId ;
    bool isJoined ;
    } ;

    // ...
    extern "C"
    void*
    executableThreadStarter(
    void* theExecutable )
    {
    static_cast< Executable >( theExecutable )->run() ;
    return theExecutable ;
    }

    Thread::Thread(
    Executable* exec )
    : myExecutable( exec )
    , isJoined( false )
    {
    pthread_start(
    &threadId, NULL, &executableThreadStarter,
    myExecutable ) ;
    }

    Thread::~Thread()
    {
    assert( isJoined ) ;
    }

    Executable*
    Thread::join()
    {
    assert( ! isJoined ) ;
    void* dummy ;
    pthread_join( threadId, &dummy ) ;
    return myExecutable ;
    }

    (This obviously needs far more error handling.)

    Client code then derives from Executable.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 27, 2008
    #5
  6. Dan Smithers

    Dan Smithers Guest

    Thanks for that James,

    James Kanze wrote:

    >> int rc = pthread_create( &m_thread, NULL, m_main, (void *)args);

    >
    > If this compiles, your compiler is broken.


    It didn't

    > And replacing m_main
    > with k_main shouldn't change anything here.


    It works with g++.

    > You need a
    > separate free function:
    >
    > extern "C" void*
    > threadStarter( void* args )
    > {
    > static_cast< CMyThread* >( args )->m_main() ;
    > }


    As it worked, I hadn't really considered the difficulties that other
    compilers might cause.

    > And since you're dealing with an object, you don't need the
    > additional args---they can just be member variables of the
    > object.
    >
    > Finally, of course, you can't call pthread_create from a
    > constructor of a base class without encuring race conditions; as
    > a general rule, you should never call it from the constructor of
    > a "thread" object which you expect run.
    >
    >> }

    >
    >> CMyThread::~CMyThread()
    >> {
    >> pthread_join( m_thread, NULL);

    >
    > pthread_join can block. I'm not sure its a good idea to call it
    > from a destructor. (Destructors are called during stack
    > walkback, in case of an exception. Which is generally a context
    > where you don't want to block for an indefinite time.)


    I'll have to think of a way of tidying up if exceptions are thrown.

    >> }

    >
    > The usual solution for this sort of thing is to separate the
    > thread from what it does. Not only does it work, but it's a lot
    > cleaner in the long run. Thus, you might end up with something
    > like:


    I had come up with something similar, but in reverse.

    I had a virtual class Thread that the user derives from, which contained
    an implementation class ThreadImp that is created when the thread is
    started.

    I was still passing around void* arg lists without stopping to think
    about whether I really needed to. (In the latest version I stored a void
    * arg in the Thread class.

    thanks for your suggestion.

    dan
     
    Dan Smithers, Jun 27, 2008
    #6
  7. Dan Smithers

    James Kanze Guest

    Dan Smithers wrote:
    > James Kanze wrote:


    > >> int rc = pthread_create( &m_thread, NULL, m_main, (void *)args);


    > > If this compiles, your compiler is broken.


    > It didn't


    > > And replacing m_main
    > > with k_main shouldn't change anything here.


    > It works with g++.


    That's a known bug in g++.

    [...]
    > >> CMyThread::~CMyThread()
    > >> {
    > >> pthread_join( m_thread, NULL);


    > > pthread_join can block. I'm not sure its a good idea to
    > > call it from a destructor. (Destructors are called during
    > > stack walkback, in case of an exception. Which is generally
    > > a context where you don't want to block for an indefinite
    > > time.)


    > I'll have to think of a way of tidying up if exceptions are
    > thrown.


    It is a problem:). And I don't know of a good solution.
    (Boost detaches the thread, which is even worse than doing the
    join.) What you want to do is to force the other thread to
    terminate as rapidly as possible, and then join. But at least
    at present, pthread_cancel doesn't work (portably) in C++, and
    even if it did, it's largely advisory, at least in some of it's
    aspects.

    > >> }


    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Jun 27, 2008
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Newsgroup - Ann
    Replies:
    5
    Views:
    667
    John Carson
    Jul 30, 2003
  2. slide_o_mix
    Replies:
    0
    Views:
    446
    slide_o_mix
    Oct 15, 2003
  3. Alex
    Replies:
    0
    Views:
    413
  4. Fraser Ross
    Replies:
    4
    Views:
    1,097
    Fraser Ross
    Aug 14, 2004
  5. somenath
    Replies:
    2
    Views:
    179
    somenath
    Aug 29, 2013
Loading...

Share This Page