I've been looking through tutorials on how to embed Ruby into a C++ program. I've found out how to define a class through "rb_define_class" and "rb_define_class_under" and methods through "rb_define_method". Now i need a good example that explains how to wrap an existing C++ object (pointer) with a ruby class written in C++. Example:
class MyClass
{
public:
MyClass();
void MyMethod();
};
VALUE myclass_init(VALUE self)
{
// I'd like to create a new MyClass instance and store its pointer inside "self"
}
VALUE myclass_meth(VALUE self)
{
// Now i need to retrieve the pointer to the object and call its method
}
int main(int argc, char* argv[])
{
ruby_init();
ruby_init_loadpath();
VALUE myclass = rb_define_class("MyWrapperClass", rb_cObject);
rb_define_method(myclass, "initialize", (VALUE(*)(...))myclass_init, 0);
rb_define_method(myclass, "myWrappedMethod", (VALUE(*)(...))myclass_meth, 0);
// Loading ruby script skipped..
ruby_finalize();
return 0;
}
I also need a way to handle garbage collection in order to free my wrapped object (and do other stuff). Sorry for the bad english and thanks to whoever will try to answer this question!
To integrate with Ruby's memory management, you need to implement two functions that allocate and free memory for one of your objects - neither may take parameters. Ruby will store your C++ data structure "attached" to the Ruby self VALUE, and you need to use a couple of methods to create that attachment, and to get at your C++ from self.
Your code so far was close enough that I have just filled in the gaps for you here:
class MyClass
{
public:
MyClass();
void MyMethod();
};
//////////////////////////////////////////////////////////
// The next five are the functions that you were missing
// (although you could factor this differently if you chose)
MyClass *rb_create_myclass_obj() {
return new MyClass();
}
void rb_delete_myclass_obj( MyClass *p_myclass ) {
delete p_myclass;
return;
}
VALUE myclass_as_ruby_class( MyClass *p_myclass , VALUE klass ) {
return Data_Wrap_Struct( klass, 0, rb_delete_myclass_obj, p_myclass );
}
VALUE myclass_alloc(VALUE klass) {
return myclass_as_ruby_class( rb_create_myclass_obj(), klass );
}
MyClass *get_myclass_obj( VALUE obj ) {
MyClass *p_myclass;
Data_Get_Struct( obj, MyClass, p_myclass );
return p_myclass;
}
//////////////////////////////////////////////////////////
VALUE myclass_init(VALUE self)
{
// You need do nothing here, Ruby will call myclass_alloc for
// you.
return self;
}
VALUE myclass_meth(VALUE self)
{
MyClass *p_myclass = get_myclass_obj( self );
p_myclass->MyMethod();
// If MyMethod returns some C++ structure, you will need to convert it
// Here's how to return Ruby's nil
return Qnil;
}
int main(int argc, char* argv[])
{
ruby_init();
ruby_init_loadpath();
VALUE myclass = rb_define_class("MyWrapperClass", rb_cObject);
// The alloc function is how Ruby hooks up the memory management
rb_define_alloc_func(myclass, myclass_alloc);
rb_define_method(myclass, "initialize", (VALUE(*)(...))myclass_init, 0);
rb_define_method(myclass, "myWrappedMethod", (VALUE(*)(...))myclass_meth, 0);
// Loading ruby script skipped..
ruby_finalize();
return 0;
}
Related
I have a C++ function which I want to export via NIF. It accepts and operates with custom data structures, std::vectors and the like.
I'm confused about what sequence of steps should be to convert Elixir type into C++ and back.
I'm aware about enif_make_resource(), enif_release_resource() and enif_open_resource_type()
Do they have to be used when returning data? Or only when parsing incoming parameters?
Code, partly:
static int nif_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
ErlNifResourceType* rt = enif_open_resource_type(env, nullptr, "vector_of_my_struct_s1",
vec1_dtor, ERL_NIF_RT_CREATE, nullptr);
if (rt == nullptr) {
return -1;
}
assert(vec1_res == nullptr);
vec1_res = rt;
return 0;
}
ERL_NIF_INIT(Elixir.MyApp, nif_funcs, nif_load, nullptr, nullptr, nullptr);
And function:
ERL_NIF_TERM do_cpp_calculations_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
// [........for now, parsing incoming arguments is skipped...]
std::vector<MyStructS1> native_res1 = do_cpp_calculations1(....);
ERL_NIF_TERM term;
// how to return 'native_res1' ?
// do I have to I use these functions at all?
// enif_alloc_resource(...) ?
// enif_make_resource(..) ?
// and how?
return term;
}
In the doc and this module from the official repo you have examples on how to do it.
Usually the steps you need are:
Create empty resource
Operate with the resource
Sometimes you do both in the same function.
Create empty resource:
Example from the doc:
ERL_NIF_TERM term;
MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));
/* initialize struct ... */
term = enif_make_resource(env, obj);
if (keep_a_reference_of_our_own) {
/* store 'obj' in static variable, private data or other resource object */
}
else {
enif_release_resource(obj);
/* resource now only owned by "Erlang" */
}
return term;
I'd recommend releasing the resource immediately and relying on the GC for the destructor, which should free the vector memory (you should make std::vector use enif_alloc to manage the memory), so in the end you may have something along the lines of:
static ERL_NIF_TERM create(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
void* pointer_to_resource_memory = enif_alloc_resource(vec1_res, sizeof(ResourceStruct));
// TODO Initialize the resource memory
ERL_NIF_TERM ret = enif_make_resource(env, pointer_to_resource_memory);
enif_release_resource(pointer_to_resource_memory);
return ret;
}
Operate with the resource
In order to work with it, you only need to extract the pointer from the resource:
static ERL_NIF_TERM do_stuff(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
void* pointer_to_resource_memory = NULL;
if (!enif_get_resource(env, argv[0], vec1_res, &pointer_to_resource_memory) {
return enif_make_badarg(env);
}
// TODO do stuff with the resource memory
// TODO make a term and return it, no need to create a new resource
return enif_make_int(env, 0);
}
Keep in mind that you don't need to create a new resource nor return it again if you don't want to, you're modifying the memory pointed by it.
Wrapping a std:vector in a resource
You may have noticed that in the snippets above I used only 'resource' and not 'vector', you have a choice there (my C++ is a bit rusty, though, so take the following with a grain of salt):
You can have the resource hold a pointer to the vector (safest):
typedef struct {
std::vector<MyStructS1>* vector;
} ResourceStruct;
void* pointer_to_resource_memory = enif_alloc_resource(vec1_res, sizeof(ResourceStruct));
pointer_to_resource_memory->vector = new std::vector(...) // std::vector constructor is called here
// TODO 'new' should use enif_alloc(), destroy, enif_free()
or you can have the resource be the vector (I'm not sure if this syntax is allowed, but you get the idea):
void* pointer_to_vector_object_memory = enif_alloc_resource(vec1_res, sizeof(std::vector<MyStructS1>));
// TODO: Somehow initialize a std::vector in the given memory
If I create a class in c++, it is possible to call a function of an object of this class, even if this class does not exists.
For example:
Class:
class ExampleClass
{
private:
double m_data;
public:
void readSomeData(double param)
{
m_data = param;
}
}
Any function where this class is used:
int main()
{
ExampleClass* myClass;
myClass->readSomeData(2.5);
}
Ofcourse this wouldn't function, because myClass is not defined.
To avoid such situations, I check if ExampleClass objects are a null_ptr
example:
void readSomeData(double param)
{
if(this == null_ptr)
return;
m_data = param;
}
But gcc says:
'this' pointer cannot be null in well-defined C++ code; comparison may
be assumed to always avaluate to false.
Ofcourse that is only a warning, but I think it is not nice to have this warning. Is there a better way to check if the pointer of a class is defined?
Testing it in the class is the wrong way, the warning is correct about that if your code is well defined then this must not be null, so the test should happen at the time when you call the member function:
int main()
{
ExampleClass* myClass = nullptr; // always initialize a raw pointer to ensure
// that it does not point to a random address
// ....
if (myClass != nullptr) {
myClass->readSomeData(2.5);
}
return 0;
}
If a pointer must not be null at a certain part of your code then you should do it according to CppCoreGuideline: I.12: Declare a pointer that must not be null as not_null
Micorosoft provides an Guidelines Support Library that has an implementation for not_null.
Or if possible then don't use pointers at all but std::optional.
So a code setup could look like this:
#include <gsl/gsl>
struct ExampleClass {
void readSomeData(double ){}
};
// now it is clear that myClass must not and can not be null within work_with_class
// it still could hold an invalid pointe, but thats another problem
void work_with_class(gsl::not_null<ExampleClass*> myClass) {
myClass->readSomeData(2.5);
}
int main()
{
ExampleClass* myClass = nullptr; // always initialize a raw pointer to ensure
// that it does not point to a random address
// ....
work_with_class(myClass);
return 0;
}
The best way is not use pointers at all:
int main()
{
ExampleClass myClass;
myClass.readSomeData(2.5);
}
That way there's no need for any check, and in fact, checking this inside the function is moot.
If you need nullability, use std::optional instead.
Either don't use pointers as Bartek Banachewicz has pointed out, or properly initialize and check the pointer:
int main()
{
ExampleClass* myClass= 0;
if (myClass)
myClass->readSomeData(2.5);
return 0;
}
Of course you still have to add the instantiation of the object at some point, otherwise the code is nonsense.
I've been furthering my experience in embedding Lua scripting in C++,
and I could use a hand, here.
Consider the following two classes:
// Person.hpp
#pragma once
#include <string>
class Person {
private:
std::string p_Name;
int p_Age;
public:
Person(const std::string & strName, const int & intAge)
: p_Name(strName), p_Age(intAge) { }
Person() : p_Name(""), p_Age(0) { }
std::string getName() const { return p_Name; }
int getAge() const { return p_Age; }
void setName(const std::string & strName) { p_Name = strName; }
void setAge(const int & intAge) { p_Age = intAge; }
};
... and ...
// PersonManager.hpp
#pragma once
#include "Person.hpp"
#include <vector>
class PersonManager {
// Assume that this class is a singleton, and therefore
// has no public constructor, but a static function that returns the
// singleton instance.
private:
std::vector<Person *> pm_People;
public:
bool personExists(const std::string & strName) { /* ... */ }
bool addPerson(const std::string & strName, const int & intAge) { /* ... */ }
Person * getPerson(const std::string & strName) { /* ... */ }
void removePerson(const std::string & strName) { /* ... */ }
void removeAllPeople() { /* ... */ }
};
... where getPerson checks the pm_People vector to see if the person with the specified name exists, using personExists.
Now, consider the following function that gets a Person object from Lua and returns its age.
// Lua_Person.cpp
#include "Lua_Person.hpp" // "Lua_Person.hpp" declares the function called to expose the "Person" functions to Lua.
#include "PersonManager.hpp"
#include "Person.hpp"
int lua_GetPersonAge(lua_State * LS) {
// Validate the userdata.
luaL_checktype(LS, 1, LUA_TUSERDATA);
// Get the "Person" userdata.
Person * luaPerson = reinterpret_cast<Person *>(lua_touserdata(LS, 1));
// Check to see if the Person pointer is not null.
if(luaPerson == nullptr)
luaL_error(LS, "lua_GetPersonAge: You gave me a null pointer!");
// Push the person's age onto the Lua stack.
lua_pushnumber(LS, luaPerson->getAge());
// Return that age integer.
return 1;
}
What I want to do is to get an already-instantiated and existing Person object from the PersonManager singleton, using getPerson, and expose that object to Lua,
so I can do something like this:
local testPerson = People.get("Stack Overflower")
print(testPerson:getAge())
I tried something like the code block below, to no avail:
int lua_GetPerson(lua_State * LS) {
// Validate the argument passed in.
luaL_checktype(LS, 1, LUA_TSTRING);
// Get the string.
std::string personName = lua_tostring(LS, 1);
// Verify that the person exists.
if(PersonManager::getInstance().personExists(personName) == false)
luaL_error(LS, "lua_GetPerson: No one exists with this ID: %s", personName.c_str());
// Put a new userdata into a double pointer, and assign it to the already existing "Person" object requested.
Person ** p = static_cast<Person **>(lua_newuserdata(LS, sizeof(Person *))); // <Userdata>
*p = PersonManager::getInstance().getPerson(personName);
// Put that person object into the "Meta_Person" metatable.
// Assume that metatable is created during the registration of the Person/Person Manager functions with Lua.
luaL_getmetatable(LS, "Meta_Person"); // <Metatable>, <Userdata>
lua_setmetatable(LS, -2); // <Metatable>
// Return that metatable.
return 1;
}
Can anybody lend a helping hand here, or at least point me in the right direction?
I am not using any lua wrapper libraries, just straight Lua.
Thank you.
EDIT: The functions that I use to expose my Person and PersonManager functions are as follows:
void exposePerson(lua_State * LS) {
static const luaL_reg person_functions[] = {
{ "getAge", lua_getPersonAge },
{ nullptr, nullptr }
};
luaL_newmetatable(LS, "Meta_Person");
lua_pushstring(LS, "__index");
lua_pushvalue(LS, -2);
lua_settable(LS, -3);
luaL_openlib(LS, nullptr, person_functions, 0);
}
void exposePersonManager(lua_State * LS) {
static const luaL_reg pman_functions[] = {
{ "get", lua_getPerson },
{ nullptr, nullptr }
};
luaL_openlib(LS, "People", pman_functions, 0);
lua_pop(LS, 1);
}
Let's start off the top, that is by registering PersonManager in Lua. Since it's a singleton, we'll register it as a global.
void registerPersonManager(lua_State *lua)
{
//First, we create a userdata instance, that will hold pointer to our singleton
PersonManager **pmPtr = (PersonManager**)lua_newuserdata(
lua, sizeof(PersonManager*));
*pmPtr = PersonManager::getInstance(); //Assuming that's the function that
//returns our singleton instance
//Now we create metatable for that object
luaL_newmetatable(lua, "PersonManagerMetaTable");
//You should normally check, if the table is newly created or not, but
//since it's a singleton, I won't bother.
//The table is now on the top of the stack.
//Since we want Lua to look for methods of PersonManager within the metatable,
//we must pass reference to it as "__index" metamethod
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
//lua_setfield pops the value off the top of the stack and assigns it to our
//field. Hence lua_pushvalue, which simply copies our table again on top of the stack.
//When we invoke lua_setfield, Lua pops our first reference to the table and
//stores it as "__index" field in our table, which is also on the second
//topmost position of the stack.
//This part is crucial, as without the "__index" field, Lua won't know where
//to look for methods of PersonManager
luaL_Reg personManagerFunctions[] = {
'get', lua_PersonManager_getPerson,
nullptr, nullptr
};
luaL_register(lua, 0, personManagerFunctions);
lua_setmetatable(lua, -2);
lua_setglobal(lua, "PersonManager");
}
Now we handle PersonManager's get method:
int lua_PersonManager_getPerson(lua_State *lua)
{
//Remember that first arbument should be userdata with your PersonManager
//instance, as in Lua you would call PersonManager:getPerson("Stack Overflower");
//Normally I would first check, if first parameter is userdata with metatable
//called PersonManagerMetaTable, for safety reasons
PersonManager **pmPtr = (PersonManager**)luaL_checkudata(
lua, 1, "PersonManagerMetaTable");
std::string personName = luaL_checkstring(lua, 2);
Person *person = (*pmPtr)->getPerson(personName);
if (person)
registerPerson(lua, person); //Function that registers person. After
//the function is called, the newly created instance of Person
//object is on top of the stack
else
lua_pushnil(lua);
return 1;
}
void registerPerson(lua_State *lua, Person *person)
{
//We assume that the person is a valid pointer
Person **pptr = (Person**)lua_newuserdata(lua, sizeof(Person*));
*pptr = person; //Store the pointer in userdata. You must take care to ensure
//the pointer is valid entire time Lua has access to it.
if (luaL_newmetatable(lua, "PersonMetaTable")) //This is important. Since you
//may invoke it many times, you should check, whether the table is newly
//created or it already exists
{
//The table is newly created, so we register its functions
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
luaL_Reg personFunctions[] = {
"getAge", lua_Person_getAge,
nullptr, nullptr
};
luaL_register(lua, 0, personFunctions);
}
lua_setmetatable(lua, -2);
}
And finally handling Person's getAge.
int lua_Person_getAge(lua_State *lua)
{
Person **pptr = (Person**)lua_checkudata(lua, 1, "PersonMetaTable");
lua_pushnumber(lua, (*pptr)->getAge());
return 1;
}
You should now call registerPersonManager before executing your Lua code, best just after you create new Lua state and open needed libraries.
Now within Lua, you should be able to do that:
local person = PersonManager:getPerson("Stack Overflower");
print(person:getAge());
I don't have access to either Lua or C++ at the moment to test it, but that should get you started. Please be careful with lifetime of the Person pointer you give Lua access to.
You use a full userdata that contains an entry that is pointer to a light userdata. Light userdata are values that can only be created from C/C++, they are like a number in Lua in that they don't have methods, metatable, etc. Then whenever your C++ functions get the full userdata, they get the pointer from it, which can then be used to access C++ methods of the underlying C++ object.
See Accessing Light userdata in Lua and the links there and see if you can work it out. There are also many posts on the Lua newsgroup archive that you can find via google.
Note that with SWIG to generate wrapper code for you, this task would be trivial and you could focus on your app rather than on binding C++ and Lua.
Using C++ I built a Class that has many setter functions, as well as various functions that may be called in a row during runtime.
So I end up with code that looks like:
A* a = new A();
a->setA();
a->setB();
a->setC();
...
a->doA();
a->doB();
Not, that this is bad, but I don't like typing "a->" over and over again.
So I rewrote my class definitions to look like:
class A{
public:
A();
virtual ~A();
A* setA();
A* setB();
A* setC();
A* doA();
A* doB();
// other functions
private:
// vars
};
So then I could init my class like: (method 1)
A* a = new A();
a->setA()->setB()->setC();
...
a->doA()->doB();
(which I prefer as it is easier to write)
To give a more precise implementation of this you can see my SDL Sprite C++ Class I wrote at http://ken-soft.com/?p=234
Everything seems to work just fine. However, I would be interested in any feedback to this approach.
I have noticed One problem. If i init My class like: (method 2)
A a = A();
a.setA()->setB()->setC();
...
a.doA()->doB();
Then I have various memory issues and sometimes things don't work as they should (You can see this by changing how i init all Sprite objects in main.cpp of my Sprite Demo).
Is that normal? Or should the behavior be the same?
Edit the setters are primarily to make my life easier in initialization. My main question is way method 1 and method 2 behave different for me?
Edit: Here's an example getter and setter:
Sprite* Sprite::setSpeed(int i) {
speed = i;
return this;
}
int Sprite::getSpeed() {
return speed;
}
One note unrelated to your question, the statement A a = A(); probably isn't doing what you expect. In C++, objects aren't reference types that default to null, so this statement is almost never correct. You probably want just A a;
A a creates a new instance of A, but the = A() part invokes A's copy constructor with a temporary default constructed A. If you had done just A a; it would have just created a new instance of A using the default constructor.
If you don't explicitly implement your own copy constructor for a class, the compiler will create one for you. The compiler created copy constructor will just make a carbon copy of the other object's data; this means that if you have any pointers, it won't copy the data pointed to.
So, essentially, that line is creating a new instance of A, then constructing another temporary instance of A with the default constructor, then copying the temporary A to the new A, then destructing the temporary A. If the temporary A is acquiring resources in it's constructor and de-allocating them in it's destructor, you could run into issues where your object is trying to use data that has already been deallocated, which is undefined behavior.
Take this code for example:
struct A {
A() {
myData = new int;
std::cout << "Allocated int at " << myData << std::endl;
}
~A() {
delete myData;
std::cout << "Deallocated int at " << myData << std::endl;
}
int* myData;
};
A a = A();
cout << "a.myData points to " << a.myData << std::endl;
The output will look something like:
Allocated int at 0x9FB7128
Deallocated int at 0x9FB7128
a.myData points to 0x9FB7128
As you can see, a.myData is pointing to an address that has already been deallocated. If you attempt to use the data it points to, you could be accessing completely invalid data, or even the data of some other object that took it's place in memory. And then once your a goes out of scope, it will attempt to delete the data a second time, which will cause more problems.
What you have implemented there is called fluent interface. I have mostly encountered them in scripting languages, but there is no reason you can't use in C++.
If you really, really hate calling lots of set functions, one after the other, then you may enjoy the following code, For most people, this is way overkill for the 'problem' solved.
This code demonstrates how to create a set function that can accept set classes of any number in any order.
#include "stdafx.h"
#include <stdarg.h>
// Base class for all setter classes
class cSetterBase
{
public:
// the type of setter
int myType;
// a union capable of storing any kind of data that will be required
union data_t {
int i;
float f;
double d;
} myValue;
cSetterBase( int t ) : myType( t ) {}
};
// Base class for float valued setter functions
class cSetterFloatBase : public cSetterBase
{
public:
cSetterFloatBase( int t, float v ) :
cSetterBase( t )
{ myValue.f = v; }
};
// A couple of sample setter classes with float values
class cSetterA : public cSetterFloatBase
{
public:
cSetterA( float v ) :
cSetterFloatBase( 1, v )
{}
};
// A couple of sample setter classes with float values
class cSetterB : public cSetterFloatBase
{
public:
cSetterB( float v ) :
cSetterFloatBase( 2, v )
{}
};
// this is the class that actually does something useful
class cUseful
{
public:
// set attributes using any number of setter classes of any kind
void Set( int count, ... );
// the attributes to be set
float A, B;
};
// set attributes using any setter classes
void cUseful::Set( int count, ... )
{
va_list vl;
va_start( vl, count );
for( int kv=0; kv < count; kv++ ) {
cSetterBase s = va_arg( vl, cSetterBase );
cSetterBase * ps = &s;
switch( ps->myType ) {
case 1:
A = ((cSetterA*)ps)->myValue.f; break;
case 2:
B = ((cSetterB*)ps)->myValue.f; break;
}
}
va_end(vl);
}
int _tmain(int argc, _TCHAR* argv[])
{
cUseful U;
U.Set( 2, cSetterB( 47.5 ), cSetterA( 23 ) );
printf("A = %f B = %f\n",U.A, U.B );
return 0;
}
You may consider the ConstrOpt paradigm. I first heard about this when reading the XML-RPC C/C++ lib documentation here: http://xmlrpc-c.sourceforge.net/doc/libxmlrpc++.html#constropt
Basically the idea is similar to yours, but the "ConstrOpt" paradigm uses a subclass of the one you want to instantiate. This subclass is then instantiated on the stack with default options and then the relevant parameters are set with the "reference-chain" in the same way as you do.
The constructor of the real class then uses the constrOpt class as the only constructor parameter.
This is not the most efficient solution, but can help to get a clear and safe API design.
I was commenting on an answer that thread-local storage is nice and recalled another informative discussion about exceptions where I supposed
The only special thing about the
execution environment within the throw
block is that the exception object is
referenced by rethrow.
Putting two and two together, wouldn't executing an entire thread inside a function-catch-block of its main function imbue it with thread-local storage?
It seems to work fine, albeit slowly. Is this novel or well-characterized? Is there another way of solving the problem? Was my initial premise correct? What kind of overhead does get_thread incur on your platform? What's the potential for optimization?
#include <iostream>
#include <pthread.h>
using namespace std;
struct thlocal {
string name;
thlocal( string const &n ) : name(n) {}
};
struct thread_exception_base {
thlocal &th;
thread_exception_base( thlocal &in_th ) : th( in_th ) {}
thread_exception_base( thread_exception_base const &in ) : th( in.th ) {}
};
thlocal &get_thread() throw() {
try {
throw;
} catch( thread_exception_base &local ) {
return local.th;
}
}
void print_thread() {
cerr << get_thread().name << endl;
}
void *kid( void *local_v ) try {
thlocal &local = * static_cast< thlocal * >( local_v );
throw thread_exception_base( local );
} catch( thread_exception_base & ) {
print_thread();
return NULL;
}
int main() {
thlocal local( "main" );
try {
throw thread_exception_base( local );
} catch( thread_exception_base & ) {
print_thread();
pthread_t th;
thlocal kid_local( "kid" );
pthread_create( &th, NULL, &kid, &kid_local );
pthread_join( th, NULL );
print_thread();
}
return 0;
}
This does require defining new exception classes derived from thread_exception_base, initializing the base with get_thread(), but altogether this doesn't feel like an unproductive insomnia-ridden Sunday morning…
EDIT: Looks like GCC makes three calls to pthread_getspecific in get_thread. EDIT: and a lot of nasty introspection into the stack, environment, and executable format to find the catch block I missed on the first walkthrough. This looks highly platform-dependent, as GCC is calling some libunwind from the OS. Overhead on the order of 4000 cycles. I suppose it also has to traverse the class hierarchy but that can be kept under control.
In the playful spirit of the question, I offer this horrifying nightmare creation:
class tls
{
void push(void *ptr)
{
// allocate a string to store the hex ptr
// and the hex of its own address
char *str = new char[100];
sprintf(str, " |%x|%x", ptr, str);
strtok(str, "|");
}
template <class Ptr>
Ptr *next()
{
// retrieve the next pointer token
return reinterpret_cast<Ptr *>(strtoul(strtok(0, "|"), 0, 16));
}
void *pop()
{
// retrieve (and forget) a previously stored pointer
void *ptr = next<void>();
delete[] next<char>();
return ptr;
}
// private constructor/destructor
tls() { push(0); }
~tls() { pop(); }
public:
static tls &singleton()
{
static tls i;
return i;
}
void *set(void *ptr)
{
void *old = pop();
push(ptr);
return old;
}
void *get()
{
// forget and restore on each access
void *ptr = pop();
push(ptr);
return ptr;
}
};
Taking advantage of the fact that according to the C++ standard, strtok stashes its first argument so that subsequent calls can pass 0 to retrieve further tokens from the same string, so therefore in a thread-aware implementation it must be using TLS.
example *e = new example;
tls::singleton().set(e);
example *e2 = reinterpret_cast<example *>(tls::singleton().get());
So as long as strtok is not used in the intended way anywhere else in the program, we have another spare TLS slot.
I think you're onto something here. This might even be a portable way to get data into callbacks that don't accept a user "state" variable, as you've mentioned, even apart from any explicit use of threads.
So it sounds like you've answered the question in your subject: YES.
void *kid( void *local_v ) try {
thlocal &local = * static_cast< thlocal * >( local_v );
throw local;
} catch( thlocal & ) {
print_thread();
return NULL;
}
==
void *kid (void *local_v ) { print_thread(local_v); }
I might be missing something here, but it's not a thread local storage, just unnecessarily complicated argument passing. Argument is different for each thread only because it is passed to pthread_create, not because of any exception juggling.
It turned out that I indeed was missing that GCC is producing actual thread local storage calls in this example. It actually makes the issue interesting. I'm still not quite sure whether it is a case for other compilers, and how is it different from calling thread storage directly.
I still stand by my general argument that the same data can be accessed in a more simple and straight-forward way, be it arguments, stack walking or thread local storage.
Accessing data on the current function call stack is always thread safe. That's why your code is thread safe, not because of the clever use of exceptions. Thread local storage allows us to store per-thread data and reference it outside of the immediate call stack.