Re: looking for elegant C++ abstraction around pthread_key_t...
"Chris M. Thomasson" <no@spam.invalid> wrote in message
news:rBrOk.33$kd5.23@newsfe01.iad...
Here is what I am playing around with now:
_________________________________________________________________
[...]
_________________________________________________________________
As you can see it uses compiler specific extensions in order to ensure the
procedure `tsd<T>::tsd_dtor()' has C linkage. I am doing this in order to
get around having to dynamically create a base-class, helper object and a
free extern "C" function. I can't really see any way around having to go
through that mess without resorting to compiler extensions. Humm, if I
were to use a free function, I think I would have to do something ugly
like:
_________________________________________________________________
[...]
_________________________________________________________________
This works fine, but IMVHO, its kind of messy. However, it is standard wrt
POSIX rules, and a heck of a lot more portable. Is there any way to keep
maximum portability, yet remove the need for helper classes? I suppose I
could do two versions and #ifdef them if the compiler does not support the
extensions I am looking for... Humm... Need advise!
[...]
Well, I suppose I could allow the user to pass in a free function that must
have C linkage for use as the actual TSD dtor; something like:
_________________________________________________________________
/* Simple TSD Object
______________________________________________________________*/
#include <pthread.h>
#include <cstdio>
template<typename T>
class tsd {
pthread_key_t m_key;
typedef void (func_dtor_t) (void*);
func_dtor_t* const m_fp_dtor;
public:
struct main_guard {
tsd& m_tsd;
main_guard(tsd& tsd_) : m_tsd(tsd_) {
}
~main_guard() {
m_tsd.clear();
}
};
tsd(func_dtor_t* const fp_dtor) : m_fp_dtor(fp_dtor) {
pthread_key_create(&m_key, fp_dtor);
}
~tsd() {
pthread_key_delete(m_key);
std::printf("(%p)->tsd<T>::~tsd()\nhit <ENTER> to continue...",
(void*)this);
std::fflush(stdout);
std::getchar();
}
T& instance() const {
T* obj = reinterpret_cast<T*>(pthread_getspecific(m_key));
if (! obj) {
obj = new T();
pthread_setspecific(m_key, obj);
}
return *obj;
}
void clear() {
T* obj = reinterpret_cast<T*>(pthread_getspecific(m_key));
pthread_setspecific(m_key, NULL);
if (obj) {
m_fp_dtor(obj);
}
}
};
/* Simple Usage Example
______________________________________________________________*/
#include <cassert>
extern "C" void foo_dtor(void* state);
extern "C" void foo2_dtor(void* state);
extern "C" void foo3_dtor(void* state);
static tsd<struct foo> g_foo_tsd(foo_dtor);
static tsd<struct foo2> g_foo2_tsd(foo2_dtor);
static tsd<struct foo3> g_foo3_tsd(foo3_dtor);
struct foo {
foo() {
std::printf("(%p)->foo::foo()\n", (void*)this);
}
~foo() {
std::printf("(%p)->foo::~foo()\n", (void*)this);
}
};
void foo_dtor(void* state) {
delete reinterpret_cast<foo*>(state);
}
struct foo2 {
foo2() {
std::printf("(%p)->foo2::foo2()\n", (void*)this);
}
~foo2() {
std::printf("(%p)->foo2::~foo2()\n", (void*)this);
}
};
void foo2_dtor(void* state) {
delete reinterpret_cast<foo2*>(state);
}
struct foo3 {
foo3() {
std::printf("(%p)->foo3::foo3()\n", (void*)this);
}
~foo3() {
std::printf("(%p)->foo3::~foo3()\n", (void*)this);
}
};
void foo3_dtor(void* state) {
delete reinterpret_cast<foo3*>(state);
}
extern "C" void* thread_entry(void* state) {
{
foo& f1 = g_foo_tsd.instance();
foo& f2 = g_foo_tsd.instance();
foo& f3 = g_foo_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}
{
foo2& f1 = g_foo2_tsd.instance();
foo2& f2 = g_foo2_tsd.instance();
foo2& f3 = g_foo2_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}
{
foo3& f1 = g_foo3_tsd.instance();
foo3& f2 = g_foo3_tsd.instance();
foo3& f3 = g_foo3_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}
return 0;
}
int main(void) {
{
tsd<foo>::main_guard tsd_main_guard(g_foo_tsd);
tsd<foo2>::main_guard tsd_main_guard2(g_foo2_tsd);
tsd<foo3>::main_guard tsd_main_guard3(g_foo3_tsd);
pthread_t tid[2];
pthread_create(&tid[0], NULL, thread_entry, NULL);
pthread_create(&tid[1], NULL, thread_entry, NULL);
{
foo& f1 = g_foo_tsd.instance();
foo& f2 = g_foo_tsd.instance();
foo& f3 = g_foo_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}
{
foo2& f1 = g_foo2_tsd.instance();
foo2& f2 = g_foo2_tsd.instance();
foo2& f3 = g_foo2_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}
{
foo3& f1 = g_foo3_tsd.instance();
foo3& f2 = g_foo3_tsd.instance();
foo3& f3 = g_foo3_tsd.instance();
assert(&f1 == &f2 && &f2 == &f3);
}
pthread_join(tid[1], NULL);
pthread_join(tid[0], NULL);
}
std::puts("\n\n\n__________________\nhit <ENTER> to exit...");
std::fflush(stdout);
std::getchar();
return 0;
}
_________________________________________________________________
That would solve my problem, but might not be all that user friendly. For
one, the poor users would
__always_need__ to ensure that the free function they pass to the `tsd<T>'
ctor has proper C linkage... Not to sure about this... Humm...
Any advise? I am NOT a C++ guy! I need help here...
:^(...