Desperation with Boost Threads

J

Johannes Bauer

Hello group,

I strongly hope this is on-topic here - I guess it's a language question
as Boost is more or less part of the C++ language (or at least it is a
candidate for getting into the standard). If I'm wrong here, please
point me to the correct location.

Basically I'm just trying to get a incredibly simple, stupid example to
work - and I can't. It doesn't compile:

#include <boost/thread.hpp>

class Thread {
private:
boost::thread ThreadID;
void operator()();
public:
Thread();
void Run();
virtual void Work() = 0;
virtual ~Thread();
};

Thread::Thread() {
}

void Thread::eek:perator()() {
Work();
}

void Thread::Run() {
ThreadID = boost::thread(*this);
}

Thread::~Thread() {
}

I get a bunch of compiler errors that I'm not even close to understanding:

g++ -O2 -Wall -g -DTRACE -ICommon -c -o Thread.o Thread.cpp
/usr/include/boost/noncopyable.hpp: In copy constructor
‘boost::thread::thread(const boost::thread&)’:
/usr/include/boost/noncopyable.hpp:27: error:
‘boost::noncopyable_::noncopyable::noncopyable(const
boost::noncopyable_::noncopyable&)’ is private
/usr/include/boost/thread/thread.hpp:35: error: within this context
Thread.hpp: In copy constructor ‘Thread::Thread(const Thread&)’:
Thread.hpp:6: note: synthesized method ‘boost::thread::thread(const
boost::thread&)’ first required here
Thread.cpp: In member function ‘void Thread::Run()’:
Thread.cpp:11: note: synthesized method ‘Thread::Thread(const Thread&)’
first required here
Thread.cpp:11: error: initializing argument 1 of ‘boost::function0<R,
Allocator>::function0(Functor, typename
boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value,
int>::type) [with Functor = Thread, R = void, Allocator =
std::allocator<boost::function_base>]’
Thread.cpp:11: error: cannot allocate an object of abstract type ‘Thread’
Thread.hpp:6: note: because the following virtual functions are pure
within ‘Thread’:
Thread.hpp:13: note: virtual void Thread::Work()
/usr/include/boost/noncopyable.hpp: In member function ‘boost::thread&
boost::thread::eek:perator=(const boost::thread&)’:
/usr/include/boost/noncopyable.hpp:28: error: ‘const
boost::noncopyable_::noncopyable&
boost::noncopyable_::noncopyable::eek:perator=(const
boost::noncopyable_::noncopyable&)’ is private
/usr/include/boost/thread/thread.hpp:35: error: within this context
[...]

What I want should be pretty clear: I want to be able to derive a class
from Thread to use it in such a fashion:

public mythread : public Thread {
mythread() : Thread() { }
void Work() {
while (true)
std::cerr << "Working..." << std::endl;
}
}

and then use it:

Thread x = mythread();
x.Run();

I want to use that abstraction so I can later on provide more
functionality (such as x.Abort() or such).

Can you tell me how to get that piece of code to compile? I'm desperate
after about two hours of fiddling with three lines of code :-\

Kind regards,
Johannes
 
N

Noah Roberts

Johannes said:
Hello group,

I strongly hope this is on-topic here - I guess it's a language question
as Boost is more or less part of the C++ language (or at least it is a
candidate for getting into the standard). If I'm wrong here, please
point me to the correct location.

Well, it depends on which part of boost. The boost mailing lists are
more appropriate for most boost libraries. However, boost::thread would
be on-topic here since it is the API (more or less) that will be in the
next standard.
Basically I'm just trying to get a incredibly simple, stupid example to
work - and I can't. It doesn't compile:

#include <boost/thread.hpp>

class Thread {
private:
boost::thread ThreadID;
void operator()();
public:
Thread();
void Run();
virtual void Work() = 0;
virtual ~Thread();
};

Thread::Thread() {
}

You need to initialize ThreadID here.
void Thread::eek:perator()() {
Work();
}

void Thread::Run() {
ThreadID = boost::thread(*this);

You can't do this. Threads are not "copyable" and thus not assignable.
}

Thread::~Thread() {
}

I get a bunch of compiler errors that I'm not even close to understanding:

g++ -O2 -Wall -g -DTRACE -ICommon -c -o Thread.o Thread.cpp
/usr/include/boost/noncopyable.hpp: In copy constructor
‘boost::thread::thread(const boost::thread&)’:
/usr/include/boost/noncopyable.hpp:27: error:
‘boost::noncopyable_::noncopyable::noncopyable(const
boost::noncopyable_::noncopyable&)’ is private

boost::noncopyable is meant to tell you that you can't copy this object
and are not meant to be able to.
/usr/include/boost/thread/thread.hpp:35: error: within this context
Thread.hpp: In copy constructor ‘Thread::Thread(const Thread&)’:
Thread.hpp:6: note: synthesized method ‘boost::thread::thread(const
boost::thread&)’ first required here
Thread.cpp: In member function ‘void Thread::Run()’:

You explicitly tried to copy a noncopyable object here.
Thread.cpp:11: note: synthesized method ‘Thread::Thread(const Thread&)’
first required here

The compiler generated a copy method that attempts to copy a noncopyable.
Thread.cpp:11: error: initializing argument 1 of ‘boost::function0<R,
Allocator>::function0(Functor, typename
boost::enable_if_c<boost::type_traits::ice_not<boost::is_integral<Functor>::value>::value,
int>::type) [with Functor = Thread, R = void, Allocator =
std::allocator<boost::function_base>]’
Thread.cpp:11: error: cannot allocate an object of abstract type ‘Thread’
Thread.hpp:6: note: because the following virtual functions are pure
within ‘Thread’:
Thread.hpp:13: note: virtual void Thread::Work()

Thread is abstract because you said = 0 in the declaration. Abstract
functions can have bodies but you are required to implement a subclass
that provides that function (in non-abstract form) that either overrides
super's behavior or calls it.
/usr/include/boost/noncopyable.hpp: In member function ‘boost::thread&
boost::thread::eek:perator=(const boost::thread&)’:
/usr/include/boost/noncopyable.hpp:28: error: ‘const
boost::noncopyable_::noncopyable&
boost::noncopyable_::noncopyable::eek:perator=(const
boost::noncopyable_::noncopyable&)’ is private
/usr/include/boost/thread/thread.hpp:35: error: within this context

You're compiler generated an assignment operator that's also hosed for
the same reasons as the rest.
[...]

What I want should be pretty clear: I want to be able to derive a class
from Thread to use it in such a fashion:

public mythread : public Thread {
mythread() : Thread() { }
void Work() {
while (true)
std::cerr << "Working..." << std::endl;
}
}

and then use it:

Thread x = mythread();
x.Run();

I want to use that abstraction so I can later on provide more
functionality (such as x.Abort() or such).

Can you tell me how to get that piece of code to compile? I'm desperate
after about two hours of fiddling with three lines of code :-\

Stop trying to copy threads.
 
J

Johannes Bauer

Noah said:
You can't do this. Threads are not "copyable" and thus not assignable.

OK. What I'm trying to to: Create a thread (in C-terms speaking I'd like
to do a pthread_create). How do I do it in that constellation then?
boost::noncopyable is meant to tell you that you can't copy this object
and are not meant to be able to.

Seems logical.
Thread is abstract because you said = 0 in the declaration. Abstract
functions can have bodies but you are required to implement a subclass
that provides that function (in non-abstract form) that either overrides
super's behavior or calls it.

I'm clear about that - and it is exactly what I wanted to do. I do not
want any plain Thead-class to be instanciated (thus the purely virtual
function). I want derived classes (which actually perform work in the
Work() method) to be called via the operator() as a trampoline function.
How can I achieve that?
Stop trying to copy threads.

I will. Could you tell me how to do better?

Kind regards,
Johannes
 
N

Noah Roberts

Johannes said:
OK. What I'm trying to to: Create a thread (in C-terms speaking I'd like
to do a pthread_create). How do I do it in that constellation then?

The equivalent is the constructor for boost::thread. You can only call
that constructor once. It constructs and spawns a new thread. You will
have a handle to that thread through the boost::thread instance so long
as that instance survives. You can't, however, copy that instance.

So, you're going to have to use pointers or something. There's many
ways to approach the problem. Any of the various ways you can share a
single instance and/or postpone initialization will solve your problem.

You might also look at how operator= works with move().
 
J

Johannes Bauer

Noah said:
The equivalent is the constructor for boost::thread. You can only call
that constructor once. It constructs and spawns a new thread. You will
have a handle to that thread through the boost::thread instance so long
as that instance survives. You can't, however, copy that instance.

Ah, okay, that makes perfect sense then. It seems I totally missed that
when reading the boost docs :-\

So I've changed it to employ your changes. So that I don't have to
fiddle with pointers I changed the behaviour of my Thread class to
behave just like boost::thread does. I threw out Work() and replaced it
by the operator(). Now the relevant parts look like this:

class Thread {
private:
boost::thread ThreadID;
public:
Thread();
Thread(const Thread &Other);
void operator=(const Thread &Other);

virtual void operator()();
virtual ~Thread();
};

include <iostream>

Thread::Thread() : ThreadID(*this) {
}

void Thread::eek:perator()() {
std::cerr << "NOWORK" << std::endl;
}

And an actual implementation:

Thread_Server::Thread_Server() : Thread() {
}

void Thread_Server::eek:perator()() {
std::cerr << "working" << std::endl;
while (1) {
std::cerr << "run" << std::endl;
sleep(3);
}
}

This compiles and works. However the derived classes operator() is never
called, only the base classes. This means when I start a thread:

Thread_Server x = Thread_Server();

it only outputs "NOWORK" and terminates. However, as the Thread_Server()
constructor calls the Thread() constructor which creates the actual
boost::thread with *this I assumed that it would make a lookup in the
vtable (virtual operator()!) and start the derived classes working function.

It does not, however. It is my assumption that during the dereferencing
step of *this the Derived class somehow gets lost - but I do not
understnad why exactly.

Trying to follow your advice using pointers, I also tried:

Thread_Server::Thread_Server() : Thread(this) {
}

With a new constructor in Thread():

Thread::Thread(const Thread *Derived) : ThreadID(*Derived) {
}

Which yielded exactly the same result.

Could you elaborate on why this happens?

Kind regards,
Johannes
 
T

Thomas J. Gritzan

Johannes said:
class Thread {
private:
boost::thread ThreadID;
public:
Thread();
Thread(const Thread &Other);
void operator=(const Thread &Other);

virtual void operator()();
virtual ~Thread();
}; [...]
Thread::Thread() : ThreadID(*this) {
}

void Thread::eek:perator()() {
std::cerr << "NOWORK" << std::endl;
}

And an actual implementation:

Thread_Server::Thread_Server() : Thread() {
}

void Thread_Server::eek:perator()() {
std::cerr << "working" << std::endl;
while (1) {
std::cerr << "run" << std::endl;
sleep(3);
}
}

This compiles and works. However the derived classes operator() is never
called, only the base classes. This means when I start a thread:

Thread_Server x = Thread_Server();

it only outputs "NOWORK" and terminates. However, as the Thread_Server()
constructor calls the Thread() constructor which creates the actual
boost::thread with *this I assumed that it would make a lookup in the
vtable (virtual operator()!) and start the derived classes working function.

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top