英文:
Is it valid to static_cast 'weak_ptr<SomeThing>*' to 'void*' then back to 'weak_ptr<void>*' , or do I need to use static_pointer_cast?
问题
I'm embedding Lua scripting in my program, and part of that is allowing Lua to manage the lifetime of some shared and weak pointers. I do that by constructing a pointer in some Lua managed memory (called 'userdata'). At a later time when Lua is about to garbage collect that memory, it calls back to my code with a void*
to the memory. At that point, I can cast the void*
back to a weak_ptr
and call the reset()
method before the memory gets freed.
My question is, can I get away with using the same garbage collection callback (__gc
metamethod in lua-speak) for every type of weak_ptr
? This would necessitate static_cast
'ing directly from void*
to weak_ptr<void>*
, even though I constructed a weak_ptr<something>
at that memory location. Is that valid?
Here's the relevant snippets of code:
void* p = lua_newuserdatauv(L,sizeof(std::weak_ptr<asio::io_service::strand>),0);
new(p) std::weak_ptr<asio::io_service::strand>(pSyncStrand);
luaL_getmetatable(L, "std_weak_ptr");
lua_setmetatable(L, -2);
And here is the metatable __gc
method that is called with the void*
(p
above):
luaL_newmetatable(L,"std_weak_ptr");
lua_pushcfunction(L, [](lua_State* const L) -> int
{
auto weak_ptr = static_cast<std::weak_ptr<void>*>(lua_touserdata(L,1));
weak_ptr->reset();
return 0;
});
lua_setfield(L,-2,"__gc");
Or will I need a separate metatable for every variant of weak_ptr
? My tests seem to indicate the above code works, but I want to make sure it's valid and not UB or implementation dependent.
EDIT: Here's my updated code based on Remy Lebeau's answer
The __gc
routine is updated to destroy, not just reset, the weak_ptr
luaL_newmetatable(L,"std_weak_ptr_void");
lua_pushcfunction(L, [](lua_State* const L) -> int
{
auto weak_ptr = static_cast<std::weak_ptr<void>*>(lua_touserdata(L,1));
std::destroy_at(weak_ptr);
return 0;
});
lua_setfield(L,-2,"__gc");
And here is how I store the pointer now
void* p = lua_newuserdatauv(L,sizeof(std::weak_ptr<void>),0);
new(p) std::weak_ptr<void>(pSyncStrand);
luaL_getmetatable(L, "std_weak_ptr_void");
lua_setmetatable(L, -2);
And as an addition, this is how I use it in the meantime
auto ppSync = static_cast<std::weak_ptr<void>*>(lua_touserdata(L, lua_upvalueindex(1)));
auto pSync = std::static_pointer_cast<asio::io_service::strand>(ppSync->lock());
英文:
I'm embedding Lua scripting in my program, and part of that is allowing Lua to manage the lifetime of some shared and weak pointers. I do that by constructing a pointer in some Lua managed memory (called 'userdata'). At a later time when Lua is about to garbage collect that memory, it calls back to my code with a void*
to the memory. At that point, I can cast the void*
back to a weak_ptr
and call the reset()
method before the memory gets freed.
My question is, can I get away with using the same garbage collection callback (__gc
metamethod in lua-speak) for every type of weak_ptr
? This would necessitate static_cast
'ing directly from void*
to weak_ptr<void>*
, even though I constructed a weak_ptr<something>
at that memory location. Is that valid?
Here's the relavent snips of code:
void* p = lua_newuserdatauv(L,sizeof(std::weak_ptr<asio::io_service::strand>),0);
new(p) std::weak_ptr<asio::io_service::strand>(pSyncStrand);
luaL_getmetatable(L, "std_weak_ptr");
lua_setmetatable(L, -2);
And here is the metatable __gc
method that is called with the void*
(p
above):
luaL_newmetatable(L,"std_weak_ptr");
lua_pushcfunction(L, [](lua_State* const L) -> int
{
auto weak_ptr = static_cast<std::weak_ptr<void>*>(lua_touserdata(L,1));
weak_ptr->reset();
return 0;
});
lua_setfield(L,-2,"__gc");
Or will I need a separate metatable for every variant of weak_ptr
? My tests seem to indicate the above code works, but I want to make sure it's valid and not UB or implementation dependent.
EDIT: Here's my updated code based on Remy Lebeau's answer
The __gc routine is updated to destroy, not just reset, the weak_ptr
luaL_newmetatable(L,"std_weak_ptr_void");
lua_pushcfunction(L, [](lua_State* const L) -> int
{
auto weak_ptr = static_cast<std::weak_ptr<void>*>(lua_touserdata(L,1));
std::destroy_at(weak_ptr);
return 0;
});
lua_setfield(L,-2,"__gc");
And here is how I store the pointer now
void* p = lua_newuserdatauv(L,sizeof(std::weak_ptr<void>),0);
new(p) std::weak_ptr<void>(pSyncStrand);
luaL_getmetatable(L, "std_weak_ptr_void");
lua_setmetatable(L, -2);
And as an addition, this is how I use it in the meantime
auto ppSync = static_cast<std::weak_ptr<void>*>(lua_touserdata(L, lua_upvalueindex(1)));
auto pSync = std::static_pointer_cast<asio::io_service::strand>(ppSync->lock());
答案1
得分: 3
以下是翻译好的部分:
这是将给定指针转换为void*
,然后将void*
转换为不同指针类型的未定义行为。
weak_ptr<something>
和 weak_ptr<void>
是不同且无关的类型。void*
必须转换回创建它的原始指针类型。
因此,首先将 weak_ptr<something>
转换为 weak_ptr<void>
,然后将其转换为 void*
,然后可以将 void*
转换回 weak_ptr<void>
。
另外一点要注意:由于您在由Lua分配的内存块中使用了placement-new
来创建weak_ptr
,请确保在Lua释放分配的内存之前销毁weak_ptr
。这意味着您必须知道您创建的原始weak_ptr
类型,以便可以正确调用其析构函数。在weak_ptr
上调用reset()
会释放weak_ptr
对另一个对象的引用,但不会销毁weak_ptr
本身。
英文:
It is UB to convert a given pointer to a void*
and then convert the void*
to a different pointer type.
weak_ptr<something>
and weak_ptr<void>
are separate and unrelated types. The void*
must be converted back to the original pointer type it was created from.
So, convert the weak_ptr<something>
to weak_ptr<void>
first, then convert that to void*
, then you can convert the void*
back to weak_ptr<void>
.
On a separate note: since you are using placement-new
to create the weak_ptr
in a memory block allocated by Lua, make sure you destruct the weak_ptr
before Lua frees the allocated memory. Which means you have to know the original weak_ptr
type that you created so you can call its destructor correctly. Calling reset()
on a weak_ptr
releases the reference that the weak_ptr
holds to another object. It does not destroy the weak_ptr
itself.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论