lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


于 2012-5-19 0:37, Matthias Kluwe 写道:
Hi!

2012/5/16 Pablo Pissanetzky<pablo@trickplay.com>:
Just wanted to share this idea:

void * operator new( size_t size , lua_State * L )
{
   return lua_newuserdata( L , size );
}

Then:

Foo * foo = new ( L ) Foo( constructor arguments );
"Placement new" works without overloading operator new. It's a matter
of taste if you wish to save a few keystrokes here.
although we can absolutely achieve the same effect without overloading it,
by overloading the placement new operator, we get cleaner code.
with the original placement new in C++, we must pass to it
the address of the memory block to be taken by the object.
now we just pass a lua_State * to it.

i.e., instead of writing
       Foo *foo = new (lua_newuserdata(L, sizeof(*foo))) Foo(...);
we can now just write
       Foo *foo = new (L) Foo(...);

Doing this depends on the returned memory being properly aligned to
hold an object of type Foo. Does anyone have a hint why this can be
assumed?

As I see, this may depend on the compiler and mostly on your memory allocator.
Lua place the userdata block colacated after the Udata header.
Normally the compiler would give the Udata header struct apropriate alignment.
So as long as your memory allocator always allocate aligned memory,
lua_newuserdata would return a aligned address.

In the past I've fallen back on

Foo **f = (Foo**) lua_newuserdata( L, sizeof( Foo* ) );
*f = new Foo();

(Of course, this assumes the returned memory can hold a pointer...)

In fact, most CPU can deal with unaligned memory access(load/store) instructions by hardware,
although some architecture may not guarantee it to be an atomic operation.
so I thingk the returned adress can always *hold* a pointer, even if it is not properly aligned,
in which case you may get a bit performance degradation(or race condition in case of concurrent access)

And in __gc you have to:

( static_cast<  Foo *>( lua_touserdata( L , 1 ) ) )->~Foo();


It looks elegant but also dangerous ( in a good way, I think :)

Pablo

P.S. You also have to have a delete operator to deal with exceptions.
You mean, you have to deal with ~Foo() throwing exceptions? That's not
the case, hopefully (see
http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.9).

well, Pablo should mean you have to have a delete operator to deal with a failed construction,
which would throgh an exception and unwind the stack, in which case, your delete operator can
be used to clean up the metatable, to prevent the gc from destructing the incomplete object.