Dynamic Loading of SDL

If you want to use the SDL library (or any other library for that matter) in your programs, you normally would just link with the library, for instance by -lSDL (GCC). Sometimes however, it would be more flexible to dynamically load the library at runtime. With dynamic loading, if your application supports alternative libraries (e.g.: GLUT SFML as an replacement for SDL), it can discover and use available libraries at runtime. The system your program is running on, just needs one of those libraries installed and can switch them without the need to recompile your program. This technique is probably most commonly used to load plugins into programs at runtime.

Theres a helpful Wikipedia article about Dynamic loading, they even describe how to load the SDL library in their examples. Besides that, theres an old discussion about that at lists.libsdl.org and an article at eaten by a grue (when good libraries go bad). However I didn’t find a complete example implementation for this, so I’m trying to create one with this article, although I’m not a very experienced C++ programmer.

The first thing to do is to load the library file, on POSIX/UNIX systems we do this with dlopen(), on windows we use LoadLibrary() and for closing dlclose() and FreeLibrary() respectively. The following code snippet for open/closing the library should work on both Linux and Windows systems:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifdef _WIN32
    #include <windows.h>
#else
    #include <dlfcn.h>
#endif
 
// [...]
 
#ifdef _WIN32
    HMODULE libhandle;
    libhandle = LoadLibrary("SDL.dll");
#else
    void *libhandle;
    libhandle = dlopen("libSDL.so", RTLD_LAZY);
#endif
 
// ... dlsym() and GetProcAddress() calls ...
 
#ifdef _WIN32
    FreeLibrary(libhandle);
#else
    dlclose(libhandle);
#endif

You need to link your programs to the dl library for this to work (such as -ldl in gcc).

The returned handle can be used to retrieve symbol addresses and then to convert them to function pointers. Theres dlsym() on POSIX and GetProcAddress() on Windows machines. The following code does that cross-platform for SDL_SetVideoMode():

1
2
3
4
5
6
7
8
SDL_Surface * (*p_SDL_SetVideoMode)(int, int, int, Uint32);
#ifdef _WIN32
    p_SDL_SetVideoMode = 
        (SDL_Surface * (*)(int, int, int, Uint32)) GetProcAddress(libhandle, "SDL_SetVideoMode");
#else
    p_SDL_SetVideoMode = 
        (SDL_Surface * (*)(int, int, int, Uint32)) dlsym(libhandle, "SDL_SetVideoMode");
#endif

You need to look at the header files (/usr/include/SDL/SDL_video.h) for the exact declaration (return and parameter types).

Theres a problem here, when casting from an void pointer that is returned by dlsym() to a function pointer which is prohibited by the C/C++ ISO standards. I’m using a simple explicit (C-style) cast here and in practice, I not even encountered a warning with modern compilers.

Johan Petersson has written an article (When standards collide: the problem with dlsym) about that problem in Scatter/Gather thoughts, I want to quote:

You probably noticed that I omitted the C-style cast from my earlier example. Alas, most C and C++ compilers will allow the conversion when you use a C-style cast. You may not even get a warning, even though it’s prohibited in ISO C as well as ISO C++. This kind of conversion is a common compiler extension. So common, in fact, that many people don’t realize it’s not in the standards.

The article is from 2004, I don’t know if the current C/C++ standards had changed any of that.

In the last code snippet theres an redundancy that can be resolved by using a typedef instead:

1
2
3
4
5
6
7
typedef SDL_Surface * (*Type_SDL_SetVideoMode)(int, int, int, Uint32);
Type_SDL_SetVideoMode p_SDL_SetVideoMode;
#ifdef _WIN32
    p_SDL_SetVideoMode = (Type_SDL_SetVideoMode) GetProcAddress(libhandle, "SDL_SetVideoMode");
#else
    p_SDL_SetVideoMode = (Type_SDL_SetVideoMode) dlsym(libhandle, "SDL_SetVideoMode");
#endif

Thats everything you need to call that function: (p_SDL_SetVideoMode)(...); The major problem here is that SDL has over 200 functions that “must” be declared and addressed this way. I’ve written a ruby script for Linux that does that for the SDL headers within /usr/include/SDL, at first it reads the symbol names directly from the libSDL.so shared library utilizing nm, then it parses the header files for the function declarations found by nm and generates the code for the pointer declaration and explicit casts (/dlsym calls) automatically.

I’ve created some example code that defines an abstract class DLLoader and an implementation DLLoaderSDL that includes an struct with the automatically generated function pointers etc. The test program loads the library and shows a SDL window for 3 seconds.
I’ve tested the program with GCC 4.5.0 (Linux), GCC 3.4.5 (Windows MinGW) and CL 14.0 (Windows VC++). I also added Makefiles for each of those compiler suites.

Update: I’m currently working on a new project that involves this dynamic loading and I’ve created a much more improved and flexible ruby script for parsing header files and built dynamic loader scripts:

Usage: src/dlib/dlib_generator.rb [OPTIONS]
Parse shared library binary and headers to generate code for a dynamic loader.

You need at least specify a name, a header file (or directory) and a prototype
pattern. The binary options are optional, but can help to improve the result.

Generator Options:
    -n, --name NAME                  The name used to identify the library
    -t, --template FILE              Use FILE as generator template 
                                     (Default: dlib_generator.tpl)
    -c, --config FILE                Use FILE for configuration, overwrites other options
    -w, --write [FILE]               Write generated code to FILE, in addition to stdout
                                     (Default file: CLibraryLoader#{options.name}.hpp)
                                     Need to be specified after name!
    -d, --define                     Create a C macro for each procedure
    -i, --include a.hpp,b.hpp        List include header files.

Library Header Options:
    -f, --header_file FILE           Use FILE or PATH for header declarations
    -p, --header_path PATH
    -r, --file_pattern PATTERN       Parse only files in header PATH that matches
                                     PATTERN (Default: \.h$)
    -R, --prototype_pattern PATTERN  Use PATTERN to parse procedures, assumes three
                                     groups: Return, procedure name and arguments

Library Binary Options:
    -l, --library FILE               Shared library FILE parsed with nm for symbols
    -s, --symbol_prefix PREFIX       Use symbol PREFIX to extract procedure names
                                     from shared library

Miscellaneous:
    -v, --verbose                    Activates console messages
    -h, --help                       Show this message