You'll typically want states to also handle messages (assuming you use messages in your game, which you probably will at some point).
Once you have messages you also don't need to special-case render (what does that mean, anyway? camera setup? scene submission? pre-culled object submission? post-processing?) or even update.
I'm a fan of state stacks, but your implementation has some safety issues since your Lua functions are char* (lifetime issues). It's also concerning that `active` is a public variable. The `set*` naming is also weird.
Using a collection of function pointers makes some sense, though typically you'd apply a bit more C++ here and just use an interface and inheritance, e.g.
// Interface that all game states must implement
class IGameState {public:virtual ~IGameState() = default;
virtual void OnUpdate() = 0;
virtual void OnRender() = 0;
};
// a game state backed by a script
class ScriptedGameState final : public IGameState {
LuaState& _lua;
LuaFunction _update;
LuaFunction _render;
public:
ScriptGameState(LuaState& lua, LuaFunction update, LuaFunction render) :
_lua(lua), _update(update), _render(render) {}
void OnUpdate() override { _lua.Invoke(_update); }
void OnRender() override { _lua.Invoke(_render); }
};
// example native/non-script game state
class NativeMainMenu final : public IGameState {
public:
void OnUpdate() override { whatever(); }
void OnRender() override { stuff(); }
};
using GameStateStack = std::vector<std::unique_ptr<IGameState>>;