Skip to content

Instantly share code, notes, and snippets.

@Mellnik
Created March 18, 2023 21:02
Show Gist options
  • Save Mellnik/fb7227b76a32719d453cc694d97fba2a to your computer and use it in GitHub Desktop.
Save Mellnik/fb7227b76a32719d453cc694d97fba2a to your computer and use it in GitHub Desktop.
LuaBridge type alignment
template<class T>
class UserdataValue : public Userdata
{
private:
UserdataValue(UserdataValue<T> const&);
UserdataValue<T> operator=(UserdataValue<T> const&);
#if LUABRIDGE_SUPPORTS_USERALIGNEDTYPES
using AlignType = typename std::conditional<alignof(T) <= alignof(double), T, void*>::type;
static constexpr int MAX_PADDING = alignof(T) <= alignof(AlignType) ? 0 : alignof(T) - alignof(AlignType) + 1;
alignas(AlignType) unsigned char m_storage[sizeof(T) + MAX_PADDING];
#else
char m_storage[sizeof(T)];
#endif
private:
/**
Used for placement construction.
*/
UserdataValue()
{
#if LUABRIDGE_SUPPORTS_USERALIGNEDTYPES
if (MAX_PADDING > 0)
{
uintptr_t offset = reinterpret_cast<uintptr_t>(&m_storage[0]) % alignof(T);
if (offset > 0) offset = alignof(T) - offset;
assert(offset < MAX_PADDING);
m_storage[sizeof(m_storage) - 1] = static_cast<unsigned char>(offset);
}
#endif
m_p = 0;
}
~UserdataValue()
{
if (getPointer() != 0)
{
getObject()->~T();
}
}
public:
/**
Push a T via placement new.
The caller is responsible for calling placement new using the
returned uninitialized storage.
@param L A Lua state.
@returns An object referring to the newly created userdata value.
*/
static UserdataValue<T>* place(lua_State* const L)
{
UserdataValue<T>* const ud =
new (lua_newuserdata(L, sizeof(UserdataValue<T>))) UserdataValue<T>();
lua_rawgetp(L, LUA_REGISTRYINDEX, detail::getClassRegistryKey<T>());
if (!lua_istable(L, -1))
{
throw std::logic_error("The class is not registered in LuaBridge");
}
lua_setmetatable(L, -2);
return ud;
}
/**
Push T via copy construction from U.
@tparam U A container type.
@param L A Lua state.
@param u A container object reference.
*/
template<class U>
static inline void push(lua_State* const L, U const& u)
{
UserdataValue<T>* ud = place(L);
new (ud->getObject()) U(u);
ud->commit();
}
/**
Confirm object construction.
*/
void commit() { m_p = getObject(); }
private:
inline void* __storagePtr()
{
#if LUABRIDGE_SUPPORTS_USERALIGNEDTYPES
if (MAX_PADDING == 0)
{
return &m_storage[0];
}
return &m_storage[0] + m_storage[sizeof(m_storage) - 1];
#else
return &m_storage[0];
#endif
}
public:
T* getObject()
{
// If this fails to compile it means you forgot to provide
// a Container specialization for your container!
//
return reinterpret_cast<T*>(__storagePtr());
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment