Calling a virtual function, illegal call of non-static member function?

Started by
24 comments, last by rip-off 10 years, 2 months ago

I think I figured out a better way, which is C++03 compliant using std::enable_shared_from_this. It eliminates the need for std::atomic<bool> m_Quit by using the std::shared_ptr::use_count() instead, which is thread safe.

What do you think?


class ThreadSafeCommunication : public std::enable_shared_from_this<ThreadSafeCommunication>
{
[...]
};

class WorkerThread
{
public:
	WorkerThread(DWORD i)
	{
		spInterface = std::make_shared<ThreadSafeCommunication>(i);
		hThread = CreateThread(NULL, 0, WorkerThread::ThreadRoutine, spInterface.get(), NULL, NULL);
	}

	~WorkerThread(void)
	{
		if(hThread)
		{
			CloseHandle(hThread);
		}
	}

private:
	std::shared_ptr<ThreadSafeCommunication> spInterface;
	
	//Thread system
	HANDLE hThread;
	static DWORD WINAPI ThreadRoutine(LPVOID param)
	{
		std::shared_ptr<ThreadSafeCommunication> spInterface = static_cast<ThreadSafeCommunication*>(param)->shared_from_this();
		
		while(spInterface.use_count() == 2)		//Only 2 strong references, one from main and one from thread.
		{
			spInterface->TickThread();

			//do stuff
		}

		return 0;
	}
};
Advertisement


I think I figured out a better way, which is C++03 compliant using std::enable_shared_from_this.

I'm not sure what you mean, std::shared_ptr is itself part of C++11. Is C++11 not an option for you? Personally, I'd prefer to use the portable standard library over system specific code where possible.


What do you think?

You've reintroduced the race condition I was trying to address earlier.

I would be wary of a hard coded test for a use_count() of exactly two. You do not appear to have made your WorkerThread class noncopyable, so it is possible that you could end up with a higher value if you were doing something like passing it to a function by value, for example you might create one and add it to a container (though doing so would cause the thread handle to be closed multiple times). I would probably use something like while(!spInterface.unique()) instead.


I'm not sure what you mean, std::shared_ptr is itself part of C++11. Is C++11 not an option for you? Personally, I'd prefer to use the portable standard library over system specific code where possible.

Sorry, I mixed up tr1 and c++03. Visual Studio 2010 implements tr1, not c++11.


You've reintroduced the race condition I was trying to address earlier.

Where is the race condition? will WorkerThread( const WorkerThread& ); fix it?

The race is between the lifetime of the WorkerThread and the start of the ThreadRountine. If the WorkerThread object was destroyed before the ThreadRoutine gets to create a copy of the shared_ptr, then you lose!

One way to avoid it might be like so:


class ThreadSafeCommunication
{
    // ...
    std::shared_ptr<ThreadSafeCommunication> self;
};
 
class WorkerThread
{
    WorkerThread()
    {
        spInterface = std::make_shared<ThreadSafeCommunication>();
        spInterface->self = spInterface;
        hThread = CreateThread(NULL, 0, WorkerThread::ThreadRoutine, spInterface.get(), NULL, NULL);
    }
 
    // ...
 
private:
 
    // ...
 
    static DWORD WINAPI ThreadRoutine(LPVOID param)
    {
        ThreadSafeCommunication *communication = static_cast<ThreadSafeCommunication*>(param)
        std::shared_ptr<ThreadSafeCommunication> spInterface;
        std::swap(spInterface, communication->self);
 
        while(!spInterface.unique())
        {
            // ... 
        }
 
        return 0;
    }
};

This scheme will keep the ThreadSafeCommunication instance alive even if the WorkerThread has been destroyed in the mean time.

I see. I suppose this will also work?


	static DWORD WINAPI ThreadRoutine(LPVOID lpParam)
	{
		std::shared_ptr<ThreadSafeCommunication> spInterface = static_cast<ThreadSafeCommunication*>(lpParam)->self;
		static_cast<ThreadSafeCommunication*>(lpParam)->self.reset();

		while(!spInterface.unique())
		{
			spInterface->TickThread();
			//do stuff
		}

		return 0;
	}

I also tried to template the whole thing by swapping out std::shared_ptr<InstructionBase> with T. But all hell breaks loose on me. The linker starts complaining about undefined external. Something about CTRP I think. Because I template a class which is inside another templated class. It is in the ThreadRoutine when the T is passed down I can't T->TickThread();

ThreadSafeCommunication<T>

WorkerThread<T>

Is it possible to template out the std::shared_ptr<InstructionBase> as it stands now?


WorkerThread(DWORD i, LPTHREAD_START_ROUTINE lpStartAddress)
	{
		spInterface = std::make_shared<ThreadSafeCommunication<std::shared_ptr<InstructionBase> > >(i);
		spInterface->self = spInterface;
		hThread = CreateThread(NULL, 0, lpStartAddress, spInterface.get(), NULL, NULL);
	}

I suppose this will also work?

That should work.

The linker starts complaining about undefined external.


Without seeing the code and error messages, it is hard to be sure, but I would guess that the issue might be that you kept the implementation of the template in a source file. Unfortunately, C++ templates don't play nicely with such a setup.

You have three choices:
  • Place the implementation directly in the header file, either inline in the class declaration or just underneath the class.
  • If you prefer to have the implementation in a separate file for ease of browsing and maintenance, you can have a some_template.hpp.impl file, and #include it in some_template.hpp (there is no special significance to the file extension here).
  • At the end of the existing template source file, explicitly instantiate the template with each type you want to use.
I believe the first two approaches are the most common.

This topic is closed to new replies.

Advertisement