The 2024 Wheel Reinvention Jam is in 16 days. September 23-29, 2024. More info

Patching function pointers after dll reload?

Hi everyone!

When Casey did the dll autoreload system, he said it could be extended to handle things like fixing function pointers after a dll reload, but he didn't get to say how he'd do it. And well, I kinda need that functionality to do dynamic dispatch in a game I'm working on :(

Off the top of my head I can think of manual ways to do it (like storing an enum instead of the function pointers, then use the enum to switch between the function pointers...) or even piggybacking into the dll main function and manually do the patching from there, but both approaches seem fragile and kinda hackish.

I'd love to make this work in a way that doesn't add much busywork when massaging the game code into shape. Any ideas?

Thanks a bunch in advance!

(Maybe Casey already said something about this on one of the later streams, but I'm now on a 10GB/month plan and I can't afford to watch the videos every day... If that's indeed the case, I'm really sorry for the noise here)
So, mandatory first question here: what do you mean by "fixing function pointers after a DLL reload"? Like, specifically what kind of code are you writing where you are storing function pointers in the data?

- Casey
Hi Casey,

They're different "update and render" functions for the different "scenes" in the game. Menu, gameplay, scene editor, etc. Using function pointers directly this way is simpler than enums and switches, but it of course breaks down when the dll reloads :(
OK, so next obligatory question: what makes it "simpler" to use function pointers instead of using a switch statement and a set of enums?

- Casey
Well, it's just 1 line of code regardless of how many possible scenes you have. A switch statement grows with the number of functions.

Consider:
1
GameState->CurrentSceneFunc(MemPool, Platform, Renderer, Input, TimeInfo);


Versus:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
SCENE_UPDATE_FUNC((*CurrentSceneFunc));
switch(GameState->CurrentScene){
case SceneMenu:
    CurrentSceneFunc = menuUpdateAndRender;
    break;
case SceneMainGameplay:
    CurrentSceneFunc = mainGameplayUpdateAndRender;
    break;
case SceneEditor:
    CurrentSceneFunc = editorUpdateAndRender;
    break;
[...]
}
CurrentSceneFunc(MemPool, Platform, Renderer, Input, TimeInfo);


It's not a hugely important thing, but it's something I'd like to avoid if possible.
Although from your responses so far I guess there isn't really much one can do in this case..
If code verbosity is what you are worried about, pull the switch statement out into its own function that takes the enum value as argument and returns the appropriate function pointer.

You could also put all those function pointers into an array, with the enum values as indices. Either have the array as a static global variable (I *think* this should work fine with reloading, as long as you don't keep pointers into this array around) or put it into your game memory and reinitialize it after each DLL reload.
If you are prepared to use a little C++ this can be a lot simpler.

Create a pure virtual interface with all the functions you want to patch and you will only need to patch a single function which returns this interface.

Obviously you need to have a class in the engine which implements the functions, but this can be a simple bridge to your existing functions.

I do this in my engine, though the interface I return is to an object which can be queried for more interfaces.
How using C++ with virtual interface is simpler than regular C functions?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
typedef void SceneFun(arg, arg...);

SceneFun* SceneFunctions[] = {
  menuUpdateAndRender,
  mainGameplayUpdateAndRender,
  editorUpdateAndRender,
  ...
};

SceneFunctions[GameState->CurrentScene](a, b, ...);

Edited by Mārtiņš Možeiko on
Well for one thing, your functions can all have different parameter lists.

From a code stand point you don't require any special handling for functions with different parameters or even a loop to patch the functions.

To add a new function just add it to the interface and implement it. No tables of data required in either the engine or .dll.

Effectively the virtual function table is implementing all your lookups that you are currently manually patching.
Well for one thing, your functions can all have different parameter lists.
So are functions in OP question.
..and your functions can all have different return values. The entire function signature can be different - you can even have overloaded functions with the same name.
Basically there is far less code involved if you use a virtual interface.
And how you imagine using such functions with different return types?

1
2
3
Object *blah;

WhatTypeIuseHere? result = blah->CallSceneUpdate(arg1, arg2);
Icabod
Basically there is far less code involved if you use a virtual interface.


I wrote 10 lines to call 3 different functions (counting also empty lines). How many lines of code you'll need to write for C++. Far less than 10? So 3 lines of code?
Just call the function directly, so 1 line.