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?

huangapple go评论57阅读模式
英文:

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&lt;void&gt;*, even though I constructed a weak_ptr&lt;something&gt; at that memory location. Is that valid?

Here's the relavent snips of code:

void* p = lua_newuserdatauv(L,sizeof(std::weak_ptr&lt;asio::io_service::strand&gt;),0);
new(p) std::weak_ptr&lt;asio::io_service::strand&gt;(pSyncStrand);
luaL_getmetatable(L, &quot;std_weak_ptr&quot;);
lua_setmetatable(L, -2);

And here is the metatable __gc method that is called with the void* (p above):

luaL_newmetatable(L,&quot;std_weak_ptr&quot;);
lua_pushcfunction(L, [](lua_State* const L) -&gt; int
	{
		auto weak_ptr = static_cast&lt;std::weak_ptr&lt;void&gt;*&gt;(lua_touserdata(L,1));
		weak_ptr-&gt;reset();
		return 0;
	});
lua_setfield(L,-2,&quot;__gc&quot;);

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,&quot;std_weak_ptr_void&quot;);
lua_pushcfunction(L, [](lua_State* const L) -&gt; int
	{
		auto weak_ptr = static_cast&lt;std::weak_ptr&lt;void&gt;*&gt;(lua_touserdata(L,1));
		std::destroy_at(weak_ptr);
		return 0;
	});
lua_setfield(L,-2,&quot;__gc&quot;);

And here is how I store the pointer now

void* p = lua_newuserdatauv(L,sizeof(std::weak_ptr&lt;void&gt;),0);
new(p) std::weak_ptr&lt;void&gt;(pSyncStrand);
luaL_getmetatable(L, &quot;std_weak_ptr_void&quot;);
lua_setmetatable(L, -2);

And as an addition, this is how I use it in the meantime

auto ppSync = static_cast&lt;std::weak_ptr&lt;void&gt;*&gt;(lua_touserdata(L, lua_upvalueindex(1)));
auto pSync = std::static_pointer_cast&lt;asio::io_service::strand&gt;(ppSync-&gt;lock());

答案1

得分: 3

以下是翻译好的部分:

这是将给定指针转换为void*,然后将void*转换为不同指针类型的未定义行为。

weak_ptr&lt;something&gt;weak_ptr&lt;void&gt; 是不同且无关的类型。void* 必须转换回创建它的原始指针类型。

因此,首先将 weak_ptr&lt;something&gt; 转换为 weak_ptr&lt;void&gt;,然后将其转换为 void*,然后可以将 void* 转换回 weak_ptr&lt;void&gt;


另外一点要注意:由于您在由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&lt;something&gt; and weak_ptr&lt;void&gt; 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&lt;something&gt; to weak_ptr&lt;void&gt; first, then convert that to void*, then you can convert the void* back to weak_ptr&lt;void&gt;.


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.

huangapple
  • 本文由 发表于 2023年6月29日 12:46:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76578114.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定