You could always store a marker field in the metatable with a light userdata value unique to your module.
static const char *green_flavor = "green";
...
void my_setflavor(lua_State *L, void *flavor) {
lua_pushlightuserdata(L,flavor);
lua_pushlstring(L,"_flavor");
lua_rawset(L,-3);
}
void my_isflavor(lua_State *L, void *flavor) {
void *p = NULL;
lua_pushlstring(L,"_flavor");
lua_rawget(L,-2);
p = lua_touserdata(L,-1);
lua_pop(L,1);
return p == flavor;
}
Then you can use my_setflavor(L,&green_flavor)
to set the _flavor field of the table at the top of the stack, and my_isflavor(L,&red_flavor)
to test the _flavor field of the table at the top of the stack.
Used this way, the _flavor field can only take on values that can be created by code in the module that has the symbol green_flavor in scope, and looking up the field and testing its value only required one table lookup aside from retrieval of the metatable itself. Note that the value of the variable green_flavor does not matter, since only its address is actually used.
With several distinct flavor variables available to use as sentinal values, the _flavor field can be used to distinguish several related metatables.
All of that said, a natural question is "why do this at all?" After all, the metatable could easily contain all of the information it needs to get the appropriate behavior. It can readily hold functions as well as data, and those functions can be retrieved and called from C as well as Lua.