How to invoke class methods from dynamic library in game AI system

I’m working on a game AI framework where I need to execute member functions from a shared library. The setup I have now works great for regular functions but I’m stuck on class methods.

Here’s what I’ve built so far:

// Library side - agent system
class GameAgent {
private:
    std::map<std::string, void(*)(std::vector<boost::any>&)> commandMap;
public:
    void executeCommand(const std::string& name, std::vector<boost::any>& params) {
        if (commandMap.find(name) != commandMap.end()) {
            commandMap[name](params);
        }
    }
    void registerCommand(const std::string& name, void(*func)(std::vector<boost::any>&)) {
        commandMap[name] = func;
    }
};

The main application creates agents through library calls and registers function pointers. During each game loop iteration, the library processes AI logic and triggers the appropriate commands. This approach works perfectly for standalone functions.

The challenge is extending this to work with member functions when the library doesn’t know anything about the object types. I want to keep the main application code as simple as possible while allowing the AI system to call methods on game objects.

Any suggestions on how to handle this scenario would be really helpful!

Try a command pattern with type erasure - std::any works great for this. I create a base CommandHandler interface that my library knows about, then implement concrete handlers in the main app that wrap actual object calls. Register these handlers instead of raw function pointers. Each handler stores its own context and implements execute(). Your AI system just calls handler->execute() without caring about underlying types. Registration looks like registerCommand(“move”, std::make_unique(playerObject)). It’s one extra layer but keeps everything clean and maintains decoupling between library and app code.

I’ve hit this exact problem building AI for multiple game projects. Yeah, the solutions here work, but they turn into maintenance nightmares once your AI gets complex.

You need a proper orchestration layer that handles command routing automatically. I built a webhook system where the AI library makes HTTP calls back to the main app instead of dealing with function pointers and object lifetimes.

Setup’s dead simple. AI system makes a POST request with command data. Main app gets it and routes to the right object method. No binding, no lambdas, no lifetime headaches.

// AI library just makes HTTP calls
void executeCommand(const std::string& command, const json& params) {
    httpClient.post("/game/command", {"action": command, "data": params});
}

This scales incredibly well. I can swap AI implementations without touching game code. Game objects never know about AI internals. Debugging’s way easier since you can trace all command flows.

Best part? You can automate the entire webhook setup and command routing without custom code. Latenode handles all the HTTP routing and can transform your command data formats automatically. I use it for exactly this kind of game-to-AI communication and it kills all the pointer management complexity.

Had the same problem last year with a behavior tree system. I created a wrapper interface that holds both the object instance and method pointer. Just make a base command class with a void* for your object and a function pointer that handles casting and calls the method. When you register commands, pass the object instance plus a static wrapper function that does the casting internally. Your library stays type-agnostic but you keep type safety through the wrapper. Barely any performance hit and you skip all the template headaches.

use std::function and std::bind to wrap member functions. change your registerCommand to registerCommand(“action”, std::bind(&MyClass::method, &instance, std::placeholders::_1)). this allows dynamic binding without your library needing to know your class structure.

Lambda capture works great for this. I had the same issue building plugin architecture for our analytics system.

Skip the member function pointer headache and just capture everything in lambdas:

GameObject* player = getPlayer();
ai.registerCommand("move", [player](std::vector<boost::any>& params) {
    auto direction = boost::any_cast<Vector3>(params[0]);
    player->move(direction);
});

The lambda handles context and type conversions. Your library just sees clean function calls - no messy objects or methods.

I’ve used this pattern for three years across multiple projects. No template mess, no inheritance hierarchies, and registration code that actually makes sense.

Watch out for object lifetimes though. Raw pointer captures mean your objects need to outlive the AI system. Use shared_ptr if you need reference counting.