Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Makefile
CMakeCache.txt
CMakeFiles/
build/
Build/
*.dir/
*.sln
*.vcxproj
Expand Down
119 changes: 119 additions & 0 deletions Distribution/LuaBridge/LuaBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -2746,6 +2746,16 @@ template <class T, auto = typeName<T>().find_first_of('.')>
return reinterpret_cast<void*>(0x8108);
}

[[nodiscard]] inline const void* getStaticIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81cc);
}

[[nodiscard]] inline const void* getStaticNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8109);
}

template <class T>
[[nodiscard]] const void* getStaticRegistryKey() noexcept
{
Expand Down Expand Up @@ -6188,6 +6198,30 @@ inline std::optional<int> try_call_index_fallback(lua_State* L)
return std::nullopt;
}

inline std::optional<int> try_call_static_index_fallback(lua_State* L)
{
LUABRIDGE_ASSERT(lua_istable(L, -1));

lua_rawgetp_x(L, -1, getStaticIndexFallbackKey());
if (! lua_iscfunction(L, -1))
{
lua_pop(L, 1);
return std::nullopt;
}

lua_pushvalue(L, 2);
lua_call(L, 1, 1);

if (! lua_isnoneornil(L, -1))
{
lua_remove(L, -2);
return 1;
}

lua_pop(L, 1);
return std::nullopt;
}

template <bool IsObject>
inline std::optional<int> try_call_index_extensible(lua_State* L, const char* key)
{
Expand Down Expand Up @@ -6239,6 +6273,12 @@ inline int index_metamethod(lua_State* L)
if (auto result = try_call_index_fallback(L))
return *result;
}
else
{

if (auto result = try_call_static_index_fallback(L))
return *result;
}

if (lua_istable(L, 1))
{
Expand Down Expand Up @@ -6354,6 +6394,24 @@ inline std::optional<int> try_call_newindex_fallback(lua_State* L)
return 0;
}

inline std::optional<int> try_call_static_newindex_fallback(lua_State* L)
{
LUABRIDGE_ASSERT(lua_istable(L, -1));

lua_rawgetp_x(L, -1, getStaticNewIndexFallbackKey());
if (! lua_iscfunction(L, -1))
{
lua_pop(L, 1);
return std::nullopt;
}

lua_pushvalue(L, 2);
lua_pushvalue(L, 3);
lua_call(L, 2, 0);

return 0;
}

inline std::optional<int> try_call_newindex_extensible(lua_State* L, const char* key)
{
LUABRIDGE_ASSERT(key != nullptr);
Expand Down Expand Up @@ -6525,6 +6583,9 @@ inline int newindex_metamethod(lua_State* L)
else
{

if (auto result = try_call_static_newindex_fallback(L))
return *result;

if (options.test(extensibleClass))
{
if (auto result = try_call_newindex_extensible(L, key))
Expand Down Expand Up @@ -9600,6 +9661,64 @@ class Namespace : public detail::Registrar
return *this;
}

template <class Function>
auto addStaticIndexMetaMethod(Function function)
-> std::enable_if_t<!std::is_pointer_v<Function>
&& std::is_invocable_v<Function, const LuaRef&, lua_State*>, Class<T>&>
{
using FnType = decltype(function);

assertStackState();

lua_newuserdata_aligned<FnType>(L, std::move(function));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, "__index", 1);
lua_rawsetp_x(L, -2, detail::getStaticIndexFallbackKey());

return *this;
}

Class<T>& addStaticIndexMetaMethod(LuaRef (*idxf)(const LuaRef&, lua_State*))
{
using FnType = decltype(idxf);

assertStackState();

lua_pushlightuserdata(L, reinterpret_cast<void*>(idxf));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<FnType>, "__index", 1);
lua_rawsetp_x(L, -2, detail::getStaticIndexFallbackKey());

return *this;
}

template <class Function>
auto addStaticNewIndexMetaMethod(Function function)
-> std::enable_if_t<!std::is_pointer_v<Function>
&& std::is_invocable_v<Function, const LuaRef&, const LuaRef&, lua_State*>, Class<T>&>
{
using FnType = decltype(function);

assertStackState();

lua_newuserdata_aligned<FnType>(L, std::move(function));
lua_pushcclosure_x(L, &detail::invoke_proxy_functor<FnType>, "__newindex", 1);
lua_rawsetp_x(L, -2, detail::getStaticNewIndexFallbackKey());

return *this;
}

Class<T>& addStaticNewIndexMetaMethod(LuaRef (*idxf)(const LuaRef&, const LuaRef&, lua_State*))
{
using FnType = decltype(idxf);

assertStackState();

lua_pushlightuserdata(L, reinterpret_cast<void*>(idxf));
lua_pushcclosure_x(L, &detail::invoke_proxy_function<FnType>, "__newindex", 1);
lua_rawsetp_x(L, -2, detail::getStaticNewIndexFallbackKey());

return *this;
}

template <class Getter>
Class<T>& addProperty(const char* name, Getter getter, bool) = delete;

Expand Down
68 changes: 68 additions & 0 deletions Manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Contents
* [2.7 - Extending Classes](#27---extending-classes)
* [2.7.1 - Extensible Classes](#271---extensible-classes)
* [2.7.2 - Index and New Index Metamethods Fallback](#272---index-and-new-index-metamethods-fallback)
* [2.7.3 - Static Index and New Index Metamethods Fallback](#273---static-index-and-new-index-metamethods-fallback)
* [2.8 - Lua Stack](#28---lua-stack)
* [2.8.1 - Enums](#281---enums)
* [2.8.2 - lua_State](#282---lua_state)
Expand Down Expand Up @@ -881,6 +882,73 @@ propertyOne = flexi.propertyOne
assert (propertyOne == 1337, "Value is now present !")
```

### 2.7.3 - Static Index and New Index Metamethods Fallback

The same fallback mechanism is available for the *static class table* — i.e. for key lookups performed on the class name itself (e.g. `MyClass.someKey`) rather than on an instance. Use `addStaticIndexMetaMethod` and `addStaticNewIndexMetaMethod` to register the callbacks. Unlike their instance counterparts, the static callbacks receive only the key (and optionally `lua_State*`) — there is no `self` parameter.

```cpp
struct MyClass {};

std::unordered_map<std::string, int> store;

luabridge::getGlobalNamespace (L)
.beginClass<MyClass> ("MyClass")
.addStaticIndexMetaMethod ([] (const luabridge::LuaRef& key, lua_State* L) -> luabridge::LuaRef
{
auto it = store.find (key.tostring ());
if (it != store.end ())
return luabridge::LuaRef (L, it->second);

return luabridge::LuaRef (L, luabridge::LuaNil ());
})
.addStaticNewIndexMetaMethod ([] (const luabridge::LuaRef& key, const luabridge::LuaRef& value, lua_State* L) -> luabridge::LuaRef
{
if (value.isNumber ())
store[key.tostring ()] = value.unsafe_cast<int> ();
return value;
})
.endClass ();
```

Then in lua:

```lua
MyClass.dynamicProp = 42

value = MyClass.dynamicProp
assert (value == 42, "Value stored via static __newindex fallback and retrieved via static __index fallback")

missing = MyClass.nonExistingKey
assert (missing == nil, "Unknown key returns nil through the static __index fallback")
```

Existing static properties and functions registered with `addStaticProperty` / `addStaticFunction` are found *after* the fallback is consulted. If the fallback callback returns `nil` (or a nil `LuaRef`) for a given key, normal lookup continues and the real property or function is returned. Conversely, if the callback returns a non-nil value the fallback result takes priority over any registered static property with the same name.

```cpp
struct MyClass
{
static int answer () { return 42; }
};

luabridge::getGlobalNamespace (L)
.beginClass<MyClass> ("MyClass")
.addStaticFunction ("answer", &MyClass::answer)
.addStaticIndexMetaMethod ([] (const luabridge::LuaRef& /*key*/, lua_State* L) -> luabridge::LuaRef
{
// Returning nil lets the registered static function be found normally
return luabridge::LuaRef (L, luabridge::LuaNil ());
})
.endClass ();
```

Then in lua:

```lua
-- The registered static function is still callable because the fallback returned nil
result = MyClass.answer ()
assert (result == 42)
```

2.8 - Lua Stack
---------------

Expand Down
52 changes: 52 additions & 0 deletions Source/LuaBridge/detail/CFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,30 @@ inline std::optional<int> try_call_index_fallback(lua_State* L)
return std::nullopt;
}

inline std::optional<int> try_call_static_index_fallback(lua_State* L)
{
LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: mt

lua_rawgetp_x(L, -1, getStaticIndexFallbackKey()); // Stack: mt, ifb (may be nil)
if (! lua_iscfunction(L, -1))
{
lua_pop(L, 1); // Stack: mt
return std::nullopt;
}

lua_pushvalue(L, 2); // Stack: mt, ifb, arg1 (key only, no self for static)
lua_call(L, 1, 1); // Stack: mt, ifbresult

if (! lua_isnoneornil(L, -1))
{
lua_remove(L, -2); // Stack: ifbresult
return 1;
}

lua_pop(L, 1); // Stack: mt
return std::nullopt;
}

template <bool IsObject>
inline std::optional<int> try_call_index_extensible(lua_State* L, const char* key)
{
Expand Down Expand Up @@ -292,6 +316,12 @@ inline int index_metamethod(lua_State* L)
if (auto result = try_call_index_fallback(L))
return *result;
}
else
{
// Repeat the lookup in the static index fallback
if (auto result = try_call_static_index_fallback(L))
return *result;
}

// Search into self or metatable
if (lua_istable(L, 1))
Expand Down Expand Up @@ -424,6 +454,24 @@ inline std::optional<int> try_call_newindex_fallback(lua_State* L)
return 0;
}

inline std::optional<int> try_call_static_newindex_fallback(lua_State* L)
{
LUABRIDGE_ASSERT(lua_istable(L, -1)); // Stack: mt

lua_rawgetp_x(L, -1, getStaticNewIndexFallbackKey()); // Stack: mt, nifb (may be nil)
if (! lua_iscfunction(L, -1))
{
lua_pop(L, 1); // Stack: mt
return std::nullopt;
}

lua_pushvalue(L, 2); // stack: mt, nifb, arg1 (key only, no self for static)
lua_pushvalue(L, 3); // stack: mt, nifb, arg1, arg2 (value)
lua_call(L, 2, 0); // stack: mt

return 0;
}

inline std::optional<int> try_call_newindex_extensible(lua_State* L, const char* key)
{
LUABRIDGE_ASSERT(key != nullptr);
Expand Down Expand Up @@ -610,6 +658,10 @@ inline int newindex_metamethod(lua_State* L)
}
else
{
// Try in the static new index fallback
if (auto result = try_call_static_newindex_fallback(L))
return *result;

// Try in the new index extensible
if (options.test(extensibleClass))
{
Expand Down
18 changes: 18 additions & 0 deletions Source/LuaBridge/detail/ClassInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,24 @@ template <class T, auto = typeName<T>().find_first_of('.')>
return reinterpret_cast<void*>(0x8108);
}

//=================================================================================================
/**
* The key of the static index fall back in another metatable.
*/
[[nodiscard]] inline const void* getStaticIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81cc);
}

//=================================================================================================
/**
* The key of the static new index fall back in another metatable.
*/
[[nodiscard]] inline const void* getStaticNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8109);
}

//=================================================================================================
/**
* @brief Get the key for the static table in the Lua registry.
Expand Down
Loading
Loading