Overview | News | Windows | macOS | Android | iOS | Audio Formats | Components | Encoder Pack | Screenshots | Help & Support | Developer | Old Versions | Other Projects | Blog & Rants
C++: self-registering static objects published on 2026-07-22
<<previous permalink next>>
When writing a large modular C++ application, it often becomes a tedious task to maintain a central registry of all abstract modules, or services as they're called in foobar2000 terminology.
Various methods exist of generating such central registry automatically. In many popular projects, the build script takes care of this.
In foobar2000, since the very beginning, this is accomplished by creating static 'service factory' objects, that build a linked list of their instances in constructors. The linked list of instances is later transformed into a different structure where services can be efficiently found by type.
Like with many other foobar2000 design ideas, I wasn't sure if this was a good idea at the time, but there was only one way to find out.
For years, there were no apparent issues with the approach, codebase being MSVC-only and all service factories existing directly in EXE/DLL source code, not in library dependencies.
Problems started when trying to split projects declaring these objects into multiple libraries, static linked into final EXE/DLL, as I found all of my service declarations to be missing from the final binary at first.
If a C++ module inside a static library does not contain externally referenced symbols, it's entirely (per module basis) excluded from linking, even if it has static objects doing meaningful stuff in constructors. Unfortunately, typical foobar2000 service code faills into this exact category.
A platform/toolset independent solution is to create a dummy function in each module and call it from some function that linker can't discard, but it defeats the whole benefit of using self-registering objects - to avoid maintaining any central registry. On top of that, it doesn't quite feel right.
Another fix is to #include .cpp files instead of linking libraries, but that isn't any prettier is it.
If linking prebuilt .lib files, add /wholearchive:mylib.lib linker option. Also works as a C/C++ #pragma:
#pragma comment(linker, "/wholearchive:mylib.lib")
add_library(foo_sample OBJECT hello.cpp)
.. good if your whole C++ project is built via CMake, like foobar2000 for Android is.
-Wl,-force_load,<path-to-library.a>
Example for specific "myproject" Xcode dependency:
-Wl,-force_load,"$(BUILT_PRODUCTS_DIR)/libmyproject.a"
-Wl,--whole-archive to load all objects in following libraries, then -Wl,--no-whole-archive to revert to the default behavior. Note that you don't want *all* dependant libraries linked like this.
Example for specific "mylib" dependency:
-Wl,--whole-archive -lmylib -Wl,--no-whole-archive
At least service factory objects in C++ modules placed directly in EXE/DLL project have always been safe, just doing the same in static libraries requires non-obvious toolchain features to work properly.
It's a nasty surprise when third-party libraries do the same without a clear warning (yes 7-Zip I'm talking about you), causing errors that aren't obvious to debug (compiles and links OK then doesn't work).
Various methods exist of generating such central registry automatically. In many popular projects, the build script takes care of this.
In foobar2000, since the very beginning, this is accomplished by creating static 'service factory' objects, that build a linked list of their instances in constructors. The linked list of instances is later transformed into a different structure where services can be efficiently found by type.
Like with many other foobar2000 design ideas, I wasn't sure if this was a good idea at the time, but there was only one way to find out.
For years, there were no apparent issues with the approach, codebase being MSVC-only and all service factories existing directly in EXE/DLL source code, not in library dependencies.
Problems started when trying to split projects declaring these objects into multiple libraries, static linked into final EXE/DLL, as I found all of my service declarations to be missing from the final binary at first.
If a C++ module inside a static library does not contain externally referenced symbols, it's entirely (per module basis) excluded from linking, even if it has static objects doing meaningful stuff in constructors. Unfortunately, typical foobar2000 service code faills into this exact category.
A platform/toolset independent solution is to create a dummy function in each module and call it from some function that linker can't discard, but it defeats the whole benefit of using self-registering objects - to avoid maintaining any central registry. On top of that, it doesn't quite feel right.
Another fix is to #include .cpp files instead of linking libraries, but that isn't any prettier is it.
Workarounds
Here's my list of verified-working fixes to ensure your objects aren't discarded when linking your app.Microsoft Visual Studio
If linking static library as project reference, set "Link Library Dependency Inputs" in references properties to true. This causes object files, not static library file, to be linked into final binary, preventing individual object files being skipped as unreferenced.If linking prebuilt .lib files, add /wholearchive:mylib.lib linker option. Also works as a C/C++ #pragma:
#pragma comment(linker, "/wholearchive:mylib.lib")
CMake
Object Library achieves the same:add_library(foo_sample OBJECT hello.cpp)
.. good if your whole C++ project is built via CMake, like foobar2000 for Android is.
Xcode and Apple linker in general
Add linker flags:-Wl,-force_load,<path-to-library.a>
Example for specific "myproject" Xcode dependency:
-Wl,-force_load,"$(BUILT_PRODUCTS_DIR)/libmyproject.a"
GNU ld
Add linker flags:-Wl,--whole-archive to load all objects in following libraries, then -Wl,--no-whole-archive to revert to the default behavior. Note that you don't want *all* dependant libraries linked like this.
Example for specific "mylib" dependency:
-Wl,--whole-archive -lmylib -Wl,--no-whole-archive
The future?
I really wish there was some C++ keyword to mitigate this. Because my work heavily relies on this, I research the subject periodically and check if a better toolchain-independent solution exists. However the problem seems to be linker issue more than C++.At least service factory objects in C++ modules placed directly in EXE/DLL project have always been safe, just doing the same in static libraries requires non-obvious toolchain features to work properly.
It's a nasty surprise when third-party libraries do the same without a clear warning (yes 7-Zip I'm talking about you), causing errors that aren't obvious to debug (compiles and links OK then doesn't work).
