3

I'm new to Lua and LuaBridge and I'm trying to figure out if it's possible to register a templated function? I've looked online and through the LuaBridge manual to no avail. What I tried was creating a pointer to the base class, but then found out later that there's no way to cast in Lua. If anyone has any ideas on the best way to resolve this issue it would be appreciated.

template<typename T>
T* GetComponentByType()
{
    try
    {
        for (ComponentVectorWrapper::t_Component_Iter iter = m_Components_.begin(); iter != m_Components_.end(); ++iter)
            if (*iter != nullptr)
                if (T* type = dynamic_cast<T*>(*iter))
                    return type;
        throw ComponentMissingException();
    }
    catch (ComponentMissingException& e)
    {
        std::cout << e.what() << std::endl;
        __debugbreak();
    }
}

Component* getComponentByType(std::string type)
{
    if (type == "Transform")
        return GetComponentByType<TransformComponent>();

    return nullptr;
}

static void registerLua(lua_State* L)
{
    using namespace luabridge;

    getGlobalNamespace(L)
        .beginClass<GameObject>("GameObject")
        .addConstructor<void(*)(const char* name)>()
        .addData<const char*>("name", &GameObject::m_Name_, false)
        .addData<TransformComponent*>("transform", &GameObject::m_Transform)
        .addFunction("addComponent", &GameObject::registerComponent)
        .addFunction("getComponent", &GameObject::getComponentByType)
        .addFunction("removeComponent", &GameObject::removeComponent)
        .endClass();
}

Solution

Forgot to post this earlier but the solution is to determine the type from a string, from there you need to set a global in Lua and then return a reference to that global.

luabridge::LuaRef GameObject::luaGetComponent(std::string type)
{
    // Return component
    return luaGetComponentHelper(type, false, "");
}

luabridge::LuaRef GameObject::luaGetComponentHelper(std::string type, bool findAll, const char* tag)
{
    lua_State* L = (&LuaEngine::getInstance())->L();

    // Find component type
    if (type == "TransformComponent")
        LuaHelper::GetGlobalComponent<TransformComponent>(*this, findAll, m_CompName, tag);
    else if (type == "CameraComponent")
        LuaHelper::GetGlobalComponent<CameraComponent>(*this, findAll, m_CompName, tag);
    else if (type == "FirstPersonCameraComponent")
        LuaHelper::GetGlobalComponent<FirstPersonCameraComponent>(*this, findAll, m_CompName, tag);
    else if (type == "RenderComponent")
        LuaHelper::GetGlobalComponent<RenderComponent>(*this, findAll, m_CompName, tag);
    else if (type == "ThirdPersonCameraComponent")
        LuaHelper::GetGlobalComponent<ThirdPersonCameraComponent>(*this, findAll, m_CompName, tag);
    else if (type == "CanvasComponent")
        LuaHelper::GetGlobalComponent<CanvasComponent>(*this, findAll, m_CompName, tag);
    else if (type == "RigidBody")
        LuaHelper::GetGlobalComponent<RigidBody>(*this, findAll, m_CompName, tag);
    else if (type == "BoxCollider")
        LuaHelper::GetGlobalComponent<BoxCollider>(*this, findAll, m_CompName, tag);
    else
    {
        luabridge::setGlobal(L, nullptr, m_CompName); // Prevents errors
        LuaEngine::printError("Component not found.");
    }

    // Return component
    return luabridge::getGlobal(L, m_CompName);
}

template<typename T>
static luabridge::LuaRef GetGlobalComponent(GameObject& go, bool findAll, const char* globalName, const char* tag)
{
    // Get lua state
    auto L = LuaEngine::getInstance().L();

    // Register global
    if (findAll)
    {
        auto vec = go.GetComponentsByType<T>();
        // Check for tag
        if (tag != "")
        {
            // Find by tag
            std::vector<T*> elements;

            for (auto& e : vec)
            {
                if (static_cast<Component*>(e)->getTag() == tag)
                    elements.push_back(e);
            }

            luabridge::setGlobal(L, LuaHelper::ToTable(elements), globalName);
        }
        else
            luabridge::setGlobal(L, LuaHelper::ToTable(vec), globalName);
    }
    else
        luabridge::setGlobal(L, go.GetComponentByType<T>(), globalName);

    return luabridge::getGlobal(L, globalName);
}
Harish Bhagat
  • 31
  • 1
  • 4

1 Answers1

1

You cannot register the templated function. You have to register explicit instantiations.

#include <iostream>
#include <lua.hpp>
#include <LuaBridge.h>

char const script [] =
  "local t = Test()"
  "t:test_int(123)"
  "t:test_str('Hello')";

class Test
{
public:
  template < typename T >
  void test(T t) { std::cout << t << '\n'; }
};

int main()
{
  lua_State* L = luaL_newstate();
  luaL_openlibs(L);

  luabridge::getGlobalNamespace(L)
    .beginClass<Test>("Test")
      .addConstructor<void(*)(void)>()
      .addFunction("test_int", &Test::test<int>)
      .addFunction("test_str", &Test::test<char const *>)
    .endClass();

  if ( luaL_dostring(L, script) != 0)
    std::cerr << lua_tostring(L,-1) << '\n';
}

I'd suggest you use sol2 which does not have such an awful syntax (needs C++14 though).

#include <iostream>
#include <string>
#include <sol.hpp>

char const script [] =
  "local t = Test.new()"
  "t:test_int(123)"
  "t:test_str('Hello')";

class Test
{
public:
  template < typename T >
  void test(T t) { std::cout << t << '\n'; }
};

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

  L.new_usertype<Test>("Test",
    "test_int", &Test::test<int>,
    "test_str", &Test::test<std::string>
    );

  L.script(script);
}
Henri Menke
  • 10,705
  • 1
  • 24
  • 42