Thread: Threading A Member Function For Templated Class

  1. #1
    Registered User
    Join Date
    Mar 2007
    Posts
    416

    Threading A Member Function For Templated Class

    I am trying to thread a member function for my template class. This thread is meant to update everything within the class at all times, and keep things "up to date". I am having problems with...

    1) understanding exactly what the errors mean. I have an idea, but its not completely clear.
    2) Actually getting it to compile.

    I have tried going about this a few ways and searched this forum but nothing seems to work. I also tried codeproject.com for a solution, and that also did not work.

    I've noticed work arounds that use non-member functions to be a thread, that calls functions within the class instead of having a class member be the thread. I would like to avoid this, but is this the only solution?

    The error
    Code:
    error C3867: 'octalTree<ObjectType>::updateOctalTreeThread': function call missing argument list; use '&octalTree<ObjectType>::updateOctalTreeThread' to create a pointer to member
    The code, anything not defined here is defined as a private/protected member of the class.
    Code:
    // update thread, I have tried type casting the thread pointer as
    // LPTHREAD_START_ROUTINE, which gives more errors since it can't do it
    template<class ObjectType> DWORD octalTree<ObjectType>::updateOctalTreeThread(void* arg)
    {
    	destroy();
    	for(int i = 0; i < allObjects.size(); i++) {
    		add(allObjects[i], allPositions[i]);
    	}
    }
    
    // the constructor trying to create the thread
    template<class ObjectType> octalTree<ObjectType>::octalTree(void)
    {
    	root = new octalNode;
    	for(int i = 0; i < 8; i++) {
    		root->child[i] = NULL;
    	}
    	objectsPerNode = 1;
    
    	hUpdateThread = CreateThread(NULL, 0, updateOctalTreeThread, (void*)this, 0, &threadID);
    }

  2. #2
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    Well I guess I should spend another 20 minutes trying before I post next time. Here is the working version.

    Code:
    // inside the class as a protected function
    	static DWORD updateOctalTreeThread(void* arg)
    	{
    		Sleep(2000);
    		octalTree* octree = (octalTree*)arg;
    		while(true) {
    			octree->destroy();
    			for(int i = 0; i < octree->allObjects.size(); i++) {
    				octree->add(octree->allObjects[i], octree->allPositions[i]);
    			}
    		}
    		return 0;
    	}
    
    // the constructor
    template<class ObjectType> octalTree<ObjectType>::octalTree(void)
    {
    	root = new octalNode;
    	for(int i = 0; i < 8; i++) {
    		root->child[i] = NULL;
    	}
    	objectsPerNode = 1;
    
    	hUpdateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)updateOctalTreeThread, (void*)this, 0, &threadID);
    }

  3. #3
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Remove the cast.
    Does it work? Then it's right.
    Does it not work? Then it's wrong.
    Unless you have a very good reason for casting a function pointer, don't do it.

    Also, I am skeptical about this inside the class as a protected function as you mention...
    In order to create a pointer to a member function inside a class, you MUST use the syntax:
    &ClassName::FunctioName.
    Last edited by Elysia; 01-19-2010 at 02:20 PM.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  4. #4
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    Putting the following presents an error, saying it cannot convert it to type LPTHREAD_START_ROUTINE. The second one does not create this error so the cast is apparently needed, weather it's correct or not, I am not sure. The thread also behaves properly when I give it a test scenario of adding objects that I can visibly see on the screen.

    Code:
    hUpdateThread = CreateThread(NULL, 0, &octalTree::updateOctalTreeThread, (void*)this, 0, &threadID);
    
    hUpdateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&octalTree::updateOctalTreeThread, (void*)this, 0, &threadID);
    Also, doing the following presents the same error of not being able to convert the cast with the cast present.

    Code:
    // ... rest of class above...
    
    protected:
    
    	unsigned long updateOctalTreeThread(void* arg);
    	HANDLE hUpdateThread;
    	DWORD threadID;
    };
    
    template<class ObjectType> unsigned long octalTree<ObjectType>::updateOctalTreeThread(void* arg)
    {
    	Sleep(2000);
    	octalTree* octree = (octalTree*)arg;
    	while(true) {
    		octree->destroy();
    		for(int i = 0; i < octree->allObjects.size(); i++) {
    			octree->add(octree->allObjects[i], octree->allPositions[i]);
    		}
    	}
    	return 0;
    }
    
    // constructor calling the thread
    
    template<class ObjectType> octalTree<ObjectType>::octalTree(void)
    {
    	root = new octalNode;
    	for(int i = 0; i < 8; i++) {
    		root->child[i] = NULL;
    	}
    	objectsPerNode = 1;
    
    	hUpdateThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&octalTree::updateOctalTreeThread, (void*)this, 0, &threadID);
    }

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    You cannot use a pointer to a member function as the argument to CreateThread(). A pointer to a function and a pointer to a member function are not the same thing.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  6. #6
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    I would like to keep the thread private, is there a way to do that yet have the thread be a non-member function? I might just make it a function and forget about privatizing anything about it.

  7. #7
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by scwizzo View Post
    I would like to keep the thread private, is there a way to do that yet have the thread be a non-member function? I might just make it a function and forget about privatizing anything about it.
    You have to jump through a hoop, either a standalone function or a static class function. The thread argument has to be the pointer to the object itself. The static function then invokes the member on the pointer to the object.

    Code:
    DWORD WINAPI LaunchMemberFunction( void *obj )
    {
        return static_cast< ThreadedClass * >( obj )->ThreadedMemberFunction();
    }
    
    ThreadedClass foo;
    CreateThread( NULL, 0, LaunchMemberFunction, &obj, 0, &threadId );
    LaunchMemberFunction could either be a stand-alone friend, or a static member.

    If you need to pass other arguments, they'll need to be stored in the object itself as member variables -- once execution transfers to ThreadedMemberFunction() these args will be accessible.

    EDIT: There are more general ways to do this, such as allowing you to specify alternate member functions to invoke... The point, though, is that the whole process needs to start in a stand-alone function, not a member function. You can probably figure it the rest out from there.
    Last edited by brewbuck; 01-19-2010 at 07:23 PM.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  8. #8
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    That makes a lot more sense, thank you.

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by scwizzo View Post
    Putting the following presents an error, saying it cannot convert it to type LPTHREAD_START_ROUTINE. The second one does not create this error so the cast is apparently needed, weather it's correct or not, I am not sure. The thread also behaves properly when I give it a test scenario of adding objects that I can visibly see on the screen.
    Like I said, if it fails to compile, it's wrong. No exceptions.
    If it works, then it's right. No cast necessary.
    You're just telling the compiler: shut up! I'm right! I do what I want! Even though the compiler is trying to say: Honestly, it's not right! Listen to me!

    Another solution is to create a static member function as an indirect call.
    I know I've created a C++-ish way of creating threads in the past, however. It should still be around the board. At least as an example.
    If you combine templates and boost::bind (or similar std functions), you can make a C++ version that can call class member functions.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    Definitely learning a lot on this one. I changed from CreateThread() to _beingthreadex(), but want to make sure it is still proper programming behavior/habit now that I have removed the type casting.

    Code:
    // rest of clas above this stuff
    
    private:
    
        unsigned int* hUpdateThread;
        unsigned int threadID;
    
        static unsigned int WINAPI update(void* arg)
        {
            octalTree* octree = (octalTree*)arg;
            while(true) {
                // do stuff
            }
            return 0;
        }
    
    };
    
    template<class ObjectType> octalTree<ObjectType>::octalTree(void)
    {
        root = new octalNode;
        for(int i = 0; i < 8; i++) {
            root->child[i] = NULL;
        }
        objectsPerNode = 1;
    
        hUpdateThread = _beginthreadex(0, 0, &octalTree::update, (void*)this, 0, &threadID);
    }
    @brewbuck: I would like to do it that way with the thread being outside the class, and the thread calling a class function. However, how would I use the pointer for a templated class like the following. I want to avoid predefining object types so that this class is as generic as possible with different objects.
    Code:
    DWORD WINAPI LaunchMemberFunction( void *obj )
    {
        return static_cast< ThreadedClass * >( obj )->ThreadedMemberFunction(); // how to define a pointer here?
        // ThreadedClass<>* ???
        // ThreadedClass* ???
    }
    
    ThreadedClass<class ObjectType> foo;
    CreateThread( NULL, 0, LaunchMemberFunction, &obj, 0, &threadId );
    @Elysia: I think I found the example you were talking about. I might have to copy paste it for later reference . Here's the thread

  11. #11
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by scwizzo View Post
    @brewbuck: I would like to do it that way with the thread being outside the class, and the thread calling a class function. However, how would I use the pointer for a templated class like the following. I want to avoid predefining object types so that this class is as generic as possible with different objects.
    The external function itself can be a template:

    Code:
    template < typename T >
    DWORD WINAPI LaunchMemberFunction( void *obj )
    {
        return static_cast< ThreadedClass<T> * >( obj )->ThreadedMemberFunction();
    }
    
    ThreadedClass<class ObjectType> foo;
    CreateThread( NULL, 0, LaunchMemberFunction<ObjectType>, &obj, 0, &threadId );
    Another way to do it is for the template to inherit from a non-template base which provides the ThreadedMemberFunction() as a pure virtual. As long as ThreadedMemberFunction takes no args that depend on the template parameter, that works fine.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  12. #12
    Registered User
    Join Date
    Mar 2007
    Posts
    416
    Thanks brewbucks, that actually presents a very nice solution. I have a thread that can update, or later comment the CreateThread() out and have the update function be accessed whenever it is needed. Maybe I'll get a small video together showing what this actually looks like in action.

    One last question. I insert objects in to this octal tree, and after about 4700+ objects it crashes with a stack overflow condition coming from the new operator. Is this coming from purely having too many objects allocated for the process? and is there a way to fix/work around this? Here is how I am adding things to the class, which is inside the thread function update().

    Code:
    // Vec3 is a 3 dimension vector, x, y, z
            Vec3* position = new Vec3(rand1*rand()%10 + 1, rand2*rand()%10 + 1, rand3*rand()%10 + 1);
            add(allObjects[0], position);
    Code:
    /* CODE FOR TEMPLATED THREADED CLASS */
    
    template<class ObjectType> DWORD WINAPI updateThread(void* arg)
    {
        octalTree<ObjectType>* octree = static_cast< octalTree<ObjectType> * >( arg );
        for(;;) octree->update();
        _endthreadex(0);
    }
    
    template<class ObjectType> class octalTree
    {
    public:
    
        octalTree(void);
        ~octalTree(void);
    
        void update();
    
    private:
    
        HANDLE hUpdateThread;
        DWORD threadID;
    
    };
    
    template<class ObjectType> octalTree<ObjectType>::octalTree(void)
    {
        hUpdateThread = CreateThread(0, 0, &updateThread<ObjectType>, (void*)this, 0, &threadID);
    }
    
    template<class ObjectType> void octalTree<ObjectType>::update()
    {
        // do stuff
    }

  13. #13
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Btw, you shouldn't have to cast this to void*, since everything is implicitly convertable to void*.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. More explicit template member function hell
    By SevenThunders in forum C++ Programming
    Replies: 1
    Last Post: 03-17-2009, 10:36 PM
  2. deriving classes
    By l2u in forum C++ Programming
    Replies: 12
    Last Post: 01-15-2007, 05:01 PM
  3. Message class ** Need help befor 12am tonight**
    By TransformedBG in forum C++ Programming
    Replies: 1
    Last Post: 11-29-2006, 11:03 PM
  4. Is it possible to have callback function as a class member?
    By Aidman in forum Windows Programming
    Replies: 11
    Last Post: 08-01-2003, 11:45 AM
  5. Staticly Bound Member Function Pointers
    By Polymorphic OOP in forum C++ Programming
    Replies: 29
    Last Post: 11-28-2002, 01:18 PM