Re: a really simple C++ abstraction around pthread_t...
"Chris M. Thomasson" <no@spam.invalid> wrote in message
news:dIpOk.15$kd5.3@newsfe01.iad...
I use the following technique in all of my C++ projects; here is the
example code with error checking omitted for brevity:
_________________________________________________________________
[...]
_________________________________________________________________
[...]
Any suggestions on how I can improve this construct?
One addition I forgot to add would be creating an explict `guard' helper
object within the `active' helper object so that one can create objects and
intervene between its ctor and when it actually gets ran... Here is full
example code showing this moment:
_________________________________________________________________
/* Simple Thread Object
______________________________________________________________*/
#include <pthread.h>
extern "C" void* thread_entry(void*);
class thread_base {
pthread_t m_tid;
friend void* thread_entry(void*);
virtual void on_active() = 0;
public:
virtual ~thread_base() = 0;
void active_run() {
pthread_create(&m_tid, NULL, thread_entry, this);
}
void active_join() {
pthread_join(m_tid, NULL);
}
};
thread_base::~thread_base() {}
void* thread_entry(void* state) {
reinterpret_cast<thread_base*>(state)->on_active();
return 0;
}
template<typename T>
struct active : public T {
struct guard {
T& m_object;
guard(T& object) : m_object(object) {
m_object.active_run();
}
~guard() {
m_object.active_join();
}
};
active() : T() {
this->active_run();
}
~active() {
this->active_join();
}
template<typename T_p1>
active(T_p1 p1) : T(p1) {
this->active_run();
}
template<typename T_p1, typename T_p2>
active(T_p1 p1, T_p2 p2) : T(p1, p2) {
this->active_run();
}
// [and on and on for more params...]
};
/* Simple Usage Example
______________________________________________________________*/
#include <string>
#include <cstdio>
class worker : public thread_base {
std::string const m_name;
void on_active() {
std::printf("(%p)->worker(%s)::on_active()\n",
(void*)this, m_name.c_str());
}
public:
worker(std::string const& name)
: m_name(name) {
std::printf("(%p)->worker(%s)::my_thread()\n",
(void*)this, m_name.c_str());
}
~worker() {
std::printf("(%p)->worker(%s)::~my_thread()\n",
(void*)this, m_name.c_str());
}
};
class another_worker : public thread_base {
unsigned const m_id;
std::string const m_name;
void on_active() {
std::printf("(%p)->another_worker(%u/%s)::on_active()\n",
(void*)this, m_id, m_name.c_str());
}
public:
another_worker(unsigned const id, std::string const& name)
: m_id(id), m_name(name) {
}
};
int main(void) {
{
worker w1("Amy");
worker w2("Kim");
worker w3("Chris");
another_worker aw1(123, "Kelly");
another_worker aw2(12345, "Tim");
another_worker aw3(87676, "John");
active<thread_base>::guard w12_aw12[] = {
w1, w2, w3,
aw1, aw2, aw3
};
active<worker> workers[] = {
"Jim",
"Dave",
"Regis"
};
active<another_worker> other_workers[] = {
active<another_worker>(999, "Jane"),
active<another_worker>(888, "Ben"),
active<another_worker>(777, "Larry")
};
}
std::puts("\n\n\n__________________\nhit <ENTER> to exit...");
std::fflush(stdout);
std::getchar();
return 0;
}
_________________________________________________________________
Take notice of the following code snippet residing within main:
worker w1("Amy");
worker w2("Kim");
worker w3("Chris");
another_worker aw1(123, "Kelly");
another_worker aw2(12345, "Tim");
another_worker aw3(87676, "John");
active<thread_base>::guard w12_aw12[] = {
w1, w2, w3,
aw1, aw2, aw3
};
This shows how one can make use of the guard object. The objects are fully
constructed, and they allow you do go ahead and do whatever you need to do
with them _before_ you actually run/join them. This can be a very convenient
ability.