sol2 icon indicating copy to clipboard operation
sol2 copied to clipboard

sol not recognizing Intrusive List as a container

Open Algebro7 opened this issue 1 year ago • 5 comments

Hello,

I'm new to Lua and only intermediate at C++ at best, so I apologize if this is a dumb question. In short, I'm trying to access a particular Intrusive List implementation from Lua, but sol throws an error when I try to iterate on it saying that it's not recognized as a container. I read some other issue threads and saw the as_container approach, which partially works:

typedef PCSX::Intrusive::List<Character> RoomListType;
[...]
lua.new_usertype<RoomListType>(
            [](RoomListType& rl) {
                return sol::as_container(rl); // Required for sol to treat Intrusive Lists as a container
            }
    );

sol no longer complains about it not being a container, and even allows me to iterate on it, but if I try to access any of the members on Character from inside Lua it bombs out:

void CharacterScript::execute() {
    sol::state lua;
    lua.new_usertype<Character>(
            "character",
            "send",
            &Character::send,
            "room",
            &Character::room, // returns a RoomListType intrusive list
            "name",
            &Character::name
    );
    lua.new_usertype<Room>(
            "room",
            "players",
            &Room::players,
            "title",
            &Room::title
    );
    lua.new_usertype<RoomListType>(
            [](RoomListType& rl) {
                return sol::as_container(rl); // Required for sol to treat Intrusive Lists as a container
            }
    );
    lua["actor"] = &m_char;
    const auto& code = R"(
        room = actor:room()
        players = room:players()
        for i = 1, #players do
             actor:send(players[i]:name())
         end
        actor:send(room:title())
    )";
    lua.script(code);

Error:

[sol2] An error occurred and has been passed to an error handler: sol: runtime error: [string "..."]:5: attempt to index a nil value (field '?')
stack traceback:
	[string "..."]:5: in main chunk
terminate called after throwing an instance of 'sol::error'
  what():  sol: runtime error: [string "..."]:5: attempt to index a nil value (field '?')
stack traceback:
	[string "..."]:5: in main chunk

Any ideas on what I'm doing wrong here? I'm confused about why the as_container bit was necessary since I believe that intrusive list class implements all the iterator functions needed to be detected as a container.

Thanks!

Algebro7 avatar Mar 20 '23 19:03 Algebro7

If I understand correctly, containers are already understood by sol if they have many characteristics of a container.

If sol determines that a type is a container, it will automatically add a bunch of container methods to it based on the kind of container it thinks it is https://sol2.readthedocs.io/en/latest/containers.html#container-detection.

//  sol should automatically detect a pushed RoomListType as a container if it seems like a container
lua.new_usertype<RoomListType>(
    // also I think this new_usertype is ill formed?
    [](RoomListType& list) {
        return sol::as_container(list);
    }
);

You should not need to create a user type for it (unless there are other con-container methods you want to add...).

ricosolana avatar Mar 24 '23 14:03 ricosolana

Thanks, I guess my question is what is missing from the IntrusiveList type that is causing sol not to recognize it as a container? As far as I can tell it implements the methods it needs like exposing iterators.

Algebro7 avatar Mar 26 '23 19:03 Algebro7

EDIT: The container has no proper push_back method (it accepts a Node), which could be why it's failing.

using StringList = PCSX::Intrusive::List<std::string>;

void TestIntrusive() {
    sol::state lua;
    lua.open_libraries();

    lua.new_usertype<StringList>("StringList",
        sol::meta_function::length, &StringList::size
        // other meta methods...
    );
}

You can manually specify the metamethods like length and pairs..., but still not sure.


Ok so I've dug deeper into this and it's not being interpreted as a container at all, even when pushed using sol::as_container.

I've even checked sol::is_container_v (which returns true). I don't get why it is not working. It could be a bug (or we're idiots).

Interestingly, compilation fails when setting the global intrusive with the std::ref and robin_hood. The PCSX list works fine though.

It's possible that the list is not being recognized because it does not contain an operator[]. All other containers implement this, and they work. The documentation lists nothing about the index operator though (std::list has no index operator but it works fine, not sure why not for the PCSX).

Here's a minimal test with many different things I've tried:

int main() {
    sol::state lua;
    lua.open_libraries();

    PCSX::Intrusive::List<std::string> intrusive;
    //robin_hood::unordered_map<std::string, std::string> intrusive;

    lua.new_usertype<PCSX::Intrusive::List<std::string>>("Intrusive");

    static_assert(sol::is_container_v<decltype(intrusive)>);

    //lua["intrusive"] = std::ref(intrusive);
    //lua["intrusive"] = &intrusive;
    //lua["intrusive"] = sol::as_container(&intrusive);
    lua["intrusive"] = sol::as_container(intrusive);

    //const auto& code = R"(
    //    for i = 1, #intrusive do
    //        print(intrusive[i])
    //    end
    //)";

    const auto& code = R"(
        for k, v in pairs(intrusive) do

        --for k, v in intrusive:pairs() do
            print(k, v)
        end
    )";

    lua.script(code);

    return 0;
}

Here's one of the errors (with all errors commonly sharing it is not recognized as a container):

[sol2] An error occurred and has been passed to an error handler: sol: runtime error: sol: cannot call '__pairs/pairs' on type 'sol::as_container_t<PCSX::Intrusive::List<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,PCSX::Intrusive::DefaultList> >': it is not recognized as a container
stack traceback:
        [C]: in ?
        [C]: in function 'pairs'
        [string "..."]:2: in main chunk

ricosolana avatar Mar 26 '23 23:03 ricosolana

So, the main issue I think is that this:

    lua.new_usertype<RoomListType>(
            [](RoomListType& rl) {
                return sol::as_container(rl); // Required for sol to treat Intrusive Lists as a container
            }
    );

gets never called. This binding here:

            "players",
            &Room::players,

which is calling

    RoomListType& players() { return m_players; }

properly does its job during the Lua code room:players(), but then after it returns, the type matching of its return type isn't triggering the RoomListType declaration, and we end up with an untyped object, which of course can't get iterated or anything.

nicolasnoble avatar Jul 16 '23 18:07 nicolasnoble

@ThePhD do you have any ideas on how we can make progress on this? Thanks!

Algebro7 avatar Jul 30 '23 18:07 Algebro7