views:

56

answers:

2

I'm trying to set up some stuff with Lua, but the specifics of Lua aren't important for my question.

What I would like to be able to do is call a function, say OpenLib<T>(L), and have it get the table name for a particular class (as well as it's table) and register it with Lua. It essentially boils down to this:

template <class T>
static void OpenLib(lua_State* L)
{
    // this func does some other stuff too that I'm omitting, important bit below
    if (T::myTable && T::myTableName)
    {
        luaL_openlib(L, T::myTableName, T::myTable, 0);
    }
}

I've tried this a few different ways and I can't get it to work right. I tried making a base class that contains myTable and myTableName like so:

class LuaInfo
{
public:
    static const char* myTableName;
    static luaL_reg* myTable;
}

Then I could just inherit from LuaInfo, and then fill in the info that I needed. That didn't work because all classes that inherit from LuaInfo would get the same info, so I looked around and got the idea of doing this:

template <class t>
class LuaInfo
// ...

Which made the syntax to initialize it a little silly as I now have to do class Widget : public LuaInfo, but it was closer to working.

template <class T>
void OpenLib(lua_State* L)
{
    if (T::myTable && T::myTableName)
    {
        luaL_openlib(L, LuaInfo<T>::myTableName, LuaInfo<T>::myTable, 0);
    }
}

I've tried a few variants of this to try to get it right but I keep getting errors like

undefined reference to `ag::LuaInfo<ag::ui::Widget>::myTable'

Is what I want to do possible, and if so, whats the right way to go about doing it?

A: 

Your first attempt works fine for me. I guess you just forgot to initialize the static members and got some link errors about that. This is what I did:

template <class T>
static void OpenLib(lua_State* L)
{
    // this func does some other stuff too that I'm omitting, important bit below
    if (T::myTable && T::myTableName)
    {
        luaL_openlib(L, T::myTableName, T::myTable, 0);
    }
}

class LuaInfo
{
    public:
        static const char* myTableName;
        static luaL_reg* myTable;
};

//init static members
const char*  LuaInfo::myTableName = 0;
luaL_reg* LuaInfo::myTable = 0;

int main()
{
    OpenLib<LuaInfo>(0);
}

Now if you want to give other info to OpenLib you have to create a new class like LuaInfo and give that new class as template parameters.

But why do you want this info as a template parameters? IMO, this is much simpler:

struct LuaInfo
{
    const char* myTableName;
    luaL_reg* myTable;
};

static void OpenLib(lua_State* L, LuaInfo info)
{
    // this func does some other stuff too that I'm omitting, important bit below
    if (info.myTable && info.myTableName)
    {
        luaL_openlib(L, info.myTableName, info.myTable, 0);
    }
}

int main()
{
    LuaInfo info = {/*some values here*/};
    OpenLib(0, info);
}
Job
+1  A: 

Using

template<typename T>
class LuaInfo
{
  static const char* myTableName;
  static lua_reg* myTable;
};

should work OK.

Your problem is that you need to define your static variables.

A single source file containing a bunch of lines like this will solve it

luaL_reg* LuaInfo<ag::ui::Widget>::myTable = 0;
const char * LuaInfo<ag::ui::Widget>::myTableName = 0;

luaL_reg* LuaInfo<ag::ui::OtherClass>::myTable = 0;
const char * LuaInfo<ag::ui::OtherClass>::myTableName = 0;

and so on.

You may want to define a macro to make this nicer.

#define LUAINFOIMPL(X) luaL_reg* LuaInfo<X>::myTable=0; const char * LuaInfo<X>::myTableName=0
LUAINFOIMPL( ag::ui::Widget );
LUAINFOIMPL( ag::ui::OtherClass );

However its a bit ugly to scale that way. I was thinking traits style templates might solve this .. but I'm not sure they scale any better.

Michael Anderson