I'm writing a library which interfaces between kdb+ (although this question is applicable to foreign language interfaces in general), and a C++ library where most of the API calls return a std::shared_ptr
. When interfacing with most libraries with kdb+, it is typical to create objects using the library's API, then return their raw pointer as a long long
so that the kdb+ programmer can send the object back into the library however they choose.
Shared pointers make this difficult. Minimal example:
extern "C" K k_new_foo() {
// not using auto for the sake of clarity in the example
std::shared_ptr<library::Foo> ptr = library::Foo::Create();
// return the raw pointer as a long long int in a kdb object
return kj(reinterpret_cast<long long>(ptr.get()));
}
// ptr goes out of scope -> the library::Foo is freed prematurely
What I would like to know is if there is some way to extend the lifetime of the std::shared_ptr
indefinitely, or to otherwise prevent the destruction of the data it points to until the programmer manually frees it from within kdb+ using another call to this interface library. I'm well aware that what I'm asking for defeats the purpose of using smart pointers; I'd love to know a good way of handling this if such a thing exists, and is practical.
The only way to extend the lifetime of such a shared object is to store the shared_ptr
in memory until the shared object is no longer needed.
For instance, you could new
a separate std::shared_ptr
and return that to the foreign language, and then delete
it when you are done using it:
using Foo_sharedptr = std::shared_ptr<library::Foo>;
extern "C" K k_new_foo() {
Foo_sharedptr ptr = library::Foo::Create();
Foo_sharedptr *ptr2 = new Foo_sharedptr(ptr);
return kj(reinterpret_cast<J>(ptr2));
}
extern "C" void k_free_foo(K foo) {
delete reinterpret_cast<Foo_sharedptr*>(foo->j);
r0(foo);
}
Alternatively, you could store the shared_ptr
in a global container that you own, and then pass around values that refer to its elements:
using Foo_ptr = library::Foo*;
using Foo_sharedptr = std::shared_ptr<library::Foo>;
using FooMap = std::map<Foo_ptr, Foo_sharedptr>;
static FooMap g_foos;
// wrapped with a std::mutex if you need multithread safety...
extern "C" K k_new_foo() {
Foo_sharedptr foo = library::Foo::Create();
Foo_ptr ptr = foo.get();
g_foos[ptr] = foo;
return kj(reinterpret_cast<J>(ptr));
}
extern "C" void k_free_foo(K foo) {
Foo_ptr ptr = reinterpret_cast<Foo_ptr>(foo->j);
FooMap::iterator iter = g_foos.find(ptr);
if (iter != g_foos.end()) g_foos.erase(iter);
r0(foo);
}