Thread: Creating A Thread W/ PThreads within an Object

  1. #1
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43

    Creating A Thread W/ PThreads within an Object

    I have a program that's been working fine, but now I'm changing it to use objects (I don't want to go into reasoning, but even if I don't change it, I want to know how to do this).

    I have been using one routine, which will be public, to call another and start the thread:
    Code:
    void * HDListen::listenthread(void *generic_pointer) {//protected
    	readinfile();
    	ioport.closeport();
    	return NULL;
    }
    
    void HDListen::listentoradio() {//public
    	pthread_create(&listenThread, NULL, listenthread, NULL);
    	return;
    }
    So far it's worked fine, but now that I'm putting these functions into an object, I'm using prototypes in the header and have this:

    Code:
    class HDListen {
    //Lots of variables that don't relate to this question...
    		 pthread_t listenThread;
    		 LinuxPort ioport;
    
    	public:
    		HDListen(LinuxPort);
    		void listentoradio();
    
    	protected:
    		void *listenthread(void*);
    };
    It seems no matte how I tweak the prototype of listenthread(), I can't get it right so it's accepted in the header file as okay and so it matches what's in the .cpp file. Why isn't it working and what do I need to change.

    And, while I'm on this issue, is it possible to make this work with passing a value to this routine or returning one -- or just not having it even return NULL?

    Thanks!

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    It seems no matte how I tweak the prototype of listenthread(), I can't get it right so it's accepted in the header file as okay and so it matches what's in the .cpp file. Why isn't it working and what do I need to change.
    You're trying to pass the address of a NON-STATIC member function. That's never going to work, because non-static functions can only be invoked through actual objects. Pthreads has no clue about this C++ stuff Instead, dispatch the thread like this:

    Code:
    // Helper function to bounce into the HDListen object
    void *StartHDListener(void *ctx)
    {
        HDListen *hdl = static_cast<HDListen *>(ctx);
        return hdl->listenthread();
    }
    
    ...
    
    pthread_create(&listenThread, NULL, StartHDListener, MyListener);
    Where MyListener is a pointer to a real HDListen object. Depending on where you are doing this, you might just pass "this" (and in your case, that's what you'd do, since the thread is being created from the HDListen object itself). Notice that this no longer requires a void * argument to the listenthread() member function.

  3. #3
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    Quote Originally Posted by brewbuck View Post
    You're trying to pass the address of a NON-STATIC member function. That's never going to work, because non-static functions can only be invoked through actual objects. Pthreads has no clue about this C++ stuff
    In a way that's a relief to hear, since it means I'm not just messing up something a simple typo!

    Quote Originally Posted by brewbuck View Post
    Instead, dispatch the thread like this:

    Code:
    // Helper function to bounce into the HDListen object
    void *StartHDListener(void *ctx)
    {
        HDListen *hdl = static_cast<HDListen *>(ctx);
        return hdl->listenthread();
    }
    
    ...
    
    pthread_create(&listenThread, NULL, StartHDListener, MyListener);
    Am I basically just getting a pointer to the HDListen object, then using that to get a pointer to the function I'm starting with?

    Could I just have that in line, like this, inside the function that creates the thread (since both the calling function and the function in the thread are in the same object):
    Code:
        HDListen *hdl = static_cast<this *>(ctx);
        pthread_create(&listenThread, NULL, hdl->listenthread(), MyListener);
    So what is ctx in this case? Just a pointer at nothing in particular?

    Quote Originally Posted by brewbuck View Post
    Where MyListener is a pointer to a real HDListen object. Depending on where you are doing this, you might just pass "this" (and in your case, that's what you'd do, since the thread is being created from the HDListen object itself). Notice that this no longer requires a void * argument to the listenthread() member function.
    Paraphrasing to be sure I've got it: I can use "this" as the 4th parameter since the function I'm calling to start the thread is in the same object as the one calling it, right?

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    Am I basically just getting a pointer to the HDListen object, then using that to get a pointer to the function I'm starting with?
    You're getting a HDListen pointer because you NEED one in order to invoke the listenthread() method. The function is a member of an object, therefore the object has to exist in order to call it.

    Could I just have that in line, like this, inside the function that creates the thread (since both the calling function and the function in the thread are in the same object):

    Code:
        HDListen *hdl = static_cast<this *>(ctx);
        pthread_create(&listenThread, NULL, hdl->listenthread(), MyListener);
    Frankly, that code's pretty much meaningless. You can't do that

    Paraphrasing to be sure I've got it: I can use "this" as the 4th parameter since the function I'm calling to start the thread is in the same object as the one calling it, right?
    Basically yes. The object which creates the thread is the same object you want to call listenthread() on in the new thread. So you just pass "this" as the argument to the thread entry point. Make sure you understand what's happening here. You pass a bare function (not a member function) as the thread entry point. The argument to the entry point is a pointer to the HDListen object which is going to run. You convert the pointer to the right type, then call listenthread() through the pointer.

  5. #5
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    I've tried compiling this with these three routines for the thread issue:
    Code:
    void HDListen::listenthread() {//protected
    	readinfile();
    	ioport.closeport();
    	return;
    }
    
    void *HDListen::StartHDListener(void *ctx) {//protected
        HDListen *hdl = static_cast<HDListen *>(ctx);
        return hdl->listenthread();
    }
    
    void HDListen::listentoradio() {//public
    	pthread_create(&listenThread, NULL, StartHDListener, this);
    	return;
    }
    I have this in the header:
    Code:
    class HDListen {
    		 pthread_t listenThread;
    		 LinuxPort ioport;
    
    	public:
    		HDListen(LinuxPort);
    		void listentoradio();
    
    	protected:
    		void listenthread();
    		void *StartHDListener(void*);
    };
    When I compile it, here's what I get:

    [hal@wizard:HD Radio Controller]$ g++ -c -lpthread -D_REENTRANT hdlisten.cpp
    hdlisten.cpp: In member function ‘void* HDListen::StartHDListener(void*)’:
    hdlisten.cpp:398: error: void value not ignored as it ought to be
    hdlisten.cpp: In member function ‘void HDListen::listentoradio()’:
    hdlisten.cpp:402: error: argument of type ‘void* (HDListen:(void*)’ does not match ‘void* (*)(void*)’

    Line 398 is the last line of StartHDListener(), with the return function, and line 402 is the line with the pthread_create() function. Should the return type be something other than void?

  6. #6
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    return hdl->listenthread();
    listenthread(); is void function, but you are trying to return its return valus (which is absent) as if it is void*
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    Line 398 is the last line of StartHDListener(), with the return function, and line 402 is the line with the pthread_create() function. Should the return type be something other than void?
    The StartHDListener() function has to be a plain old function, not a class member function. Define it outside the class.

    Your original code had the listenthread() member returning a void *, but you changed it. Either change it back (and return NULL and the end of listenthread() ), or alter the StartHDListener() function to this:

    Code:
    void *StartHDListener(void *ctx)
    {
        HDListen *hdl = static_cast<HDListen *>(ctx);
        hdl->listenthread();
        return 0;
    }
    One or the other.

  8. #8
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    Quote Originally Posted by brewbuck View Post
    Basically yes. The object which creates the thread is the same object you want to call listenthread() on in the new thread. So you just pass "this" as the argument to the thread entry point. Make sure you understand what's happening here. You pass a bare function (not a member function) as the thread entry point. The argument to the entry point is a pointer to the HDListen object which is going to run. You convert the pointer to the right type, then call listenthread() through the pointer.
    Okay, first I wanted to make sure it worked, then I went back over the code and this explanation a few times to make sure I followed what's going on.

    I've heard for years how many people just don't get pointers and how people like that are never going to do well with C or C++. Now I see why. I think the concept of a pointer itself is quite simple, but what amazes me is how so much is done with pointers instead of specifying the variable.

    Since StartHDListener is not in the class itself, are there going to be any problems with multiple instances of HDListen calling the same thread in the same instance? I wouldn't think so since the object itself is passed as a parameter, but I just want to be sure.

    Thanks, by the way, for your help and patience!

  9. #9
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    I've heard for years how many people just don't get pointers and how people like that are never going to do well with C or C++. Now I see why. I think the concept of a pointer itself is quite simple, but what amazes me is how so much is done with pointers instead of specifying the variable.
    Not just pointers, but any method of referring to another variable indirectly, such as a reference or handle... They are powerful, necessary constructs, at least in this language!

    Since StartHDListener is not in the class itself, are there going to be any problems with multiple instances of HDListen calling the same thread in the same instance? I wouldn't think so since the object itself is passed as a parameter, but I just want to be sure.
    If I understand the question, no, there are no problems like that.

  10. #10
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    Quote Originally Posted by brewbuck View Post
    You're trying to pass the address of a NON-STATIC member function. That's never going to work, because non-static functions can only be invoked through actual objects. Pthreads has no clue about this C++ stuff
    After re-reading this thread and noticing this, then it leads to another question. In this case I was trying to pass a pointer to a function and once that function was in an object, it wouldn't work. In one section of code I have:
    Code:
    	 typedef void (*Func0)();
    	 typedef map< string, Func0 > FuncMap0;
    ...
    	cmdfunc0["on"] = hd_on;
    	cmdfunc0["off"] = hd_off;
    From what I understand, once that code is in an object, even if I'll be calling the functions from within that same object, I can no longer call them by reference.

    Is that correct? Is there an easy alternative, or do I need to put a big switch or if..else statements in to call the functions?

  11. #11
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by HalNineThousand View Post
    From what I understand, once that code is in an object, even if I'll be calling the functions from within that same object, I can no longer call them by reference.
    Correct -- you will have to store something other than just bare function pointers in the map -- both a pointer-to-object and a pointer-to-member, so that you can call the member through the object pointer.

    This might get into syntax you haven't seen before:

    Code:
    class HDListen_callback
    {
    public:
        HDListen_callback(HDListen *hdl_p, void (HDListen::*func_p)())
            : hdl(hdl_p), func(func_p) {}
    
        void operator()() { (hdl->*func)(); }
    
    private:
        HDListen *hdl;
        void (HDListen::*func)();
    };
    
    ...
    
    // Somewhere inside an HDListen object...
    
    typedef HDListen_callback Func0;
    typedef std::map<std::string, Func0> FuncMap0;
    FuncMap0 cmdfunc0;
    cmdfunc0["on"] = HDListen_callback(this, &HDListen::hd_on);
    cmdfunc0["off"] = HDListen_callback(this, &HDListen::hd_off);
    
    ...
    
    cmdfunc0["on"](); // call it
    Last edited by brewbuck; 03-27-2008 at 11:43 AM.

  12. #12
    Registered User HalNineThousand's Avatar
    Join Date
    Mar 2008
    Posts
    43
    Yes, it does get into syntax I haven't seen before. Thanks! I'll put it into my program and I'm sure it'll take a bit for me to figure out how it works, but having a guide will let me understand it.

    Much appreciated!

  13. #13
    Registered User
    Join Date
    Aug 2005
    Posts
    96
    Quote Originally Posted by brewbuck View Post
    The StartHDListener() function has to be a plain old function, not a class member function. Define it outside the class.
    You could also use a static member function. That works too.

    Code:
    class HDListen
    {
    private:
        
        void listenthread();
    	
        static void* StartHDListener(void* arg);
    };
    
    void* HDListen::StartHDListener(void* arg)
    {
        HDListen* hdl = static_cast<HDListen* >(arg);
    
        hdl->listenthread();
    
        return 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 02-26-2009, 11:48 PM
  2. what's the hidden cost of creating object?
    By kypronite in forum C++ Programming
    Replies: 13
    Last Post: 10-12-2008, 04:41 PM
  3. Calling a Thread with a Function Pointer.
    By ScrollMaster in forum Windows Programming
    Replies: 6
    Last Post: 06-10-2006, 08:56 AM
  4. Question on l-values.
    By Hulag in forum C++ Programming
    Replies: 6
    Last Post: 10-13-2005, 04:33 PM
  5. Win32 Thread Object Model Revisted
    By Codeplug in forum Windows Programming
    Replies: 5
    Last Post: 12-15-2004, 08:50 AM