Add SFML_OBJC_CLASS_PREFIX option for Objective-C class name renaming by jkeller51 · Pull Request #3719 · SFML/SFML · GitHub
Skip to content

Add SFML_OBJC_CLASS_PREFIX option for Objective-C class name renaming - #3719

Open
jkeller51 wants to merge 1 commit into
SFML:masterfrom
ZivixLLC:jon-keller/objc-class-prefix
Open

jkeller51 wants to merge 1 commit into
SFML:masterfrom
ZivixLLC:jon-keller/objc-class-prefix

Conversation

@jkeller51

@jkeller51 jkeller51 commented May 27, 2026 •

Copy link
Copy Markdown
Contributor

Description

When multiple binaries embed SFML, the Objective-C runtime raises duplicate-class errors because internal SF* class names collide. This PR adds a CMake option to prefix those names at build time.

CMake will generate SFMLObjcPrefix.h via configure_file(@only). This file will effectively rename all SF* Obj-C classes. Defines one rename macro per class:

// SFML_OBJC_CLASS_PREFIX = "MyApp"
#define SFWindow             MyAppSFWindow
#define SFApplication        MyAppSFApplication
// … all 8 classes

Empty prefix (default) → no-op macros (#define SFWindow SFWindow), zero behavior change for existing builds.

This PR is related to the issue #3718

Tasks

  • Tested on Linux
  • Tested on Windows
  • Tested on macOS
  • Tested on iOS
  • Tested on Android

How to test this PR?

Build and run test-sfml-window with SFML_OBJC_CLASS_PREFIX set to empty (default). Change SFML_OBJC_CLASS_PREFIX to a custom prefix (e.g. ComMyApp) and build and run again. All tests pass.

@jkeller51 jkeller51 mentioned this pull request May 27, 2026
3 tasks done
@JonnyPtn

Copy link
Copy Markdown
Contributor

@jkeller51

Copy link
Copy Markdown
Contributor Author
  • One process loading multiple dynamic libs which each statically link different SFML versions
  • The process must lookup these classes by name at runtime

The SFML versions can be linked statically or dynamically, I believe.

And the first point here implies the second, as any code using SFML on MacOS will necessarily lead to runtime lookup of the Objective-C symbols.

The fix here can also be applied by users without any SFML code changes, by adding their own defines on the command line when building SFML

That's also a good idea.

@JonnyPtn

Copy link
Copy Markdown
Contributor

And the first point here implies the second, as any code using SFML on MacOS will necessarily lead to runtime lookup of the Objective-C symbols.

I'm not sure that's correct, although I won't claim to be an expert on the matter, my understanding is it only applies when looking up by name e.g. NSClassFromString. Direct references like [SFWindow alloc] will use the correct one. SFML never looks up classes by name and I can't imagine user code would ever do it either.

regardless I think this is pretty much a hypothetical issue, and one that users can workaround without modifying SFML source code

@jkeller51

jkeller51 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

I don't think that's quite right, the name conflict is an issue in the global symbol table at the process level, not just for runtime string lookups.

And, it's not a hypothetical issue, it's a real documented issue with recommended steps to avoid it in Apple's documentation: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingCode/Tasks/NameConflicts.html
(this PR applies Apple's recommended procedure).

Aside from Apple's documentation, I mention in the linked issue that I experienced this issue myself when running newer software my company has developed alongside older unsupported software (but still in customer use). I was able to circumvent it by applying Apple's recommended procedure from the link above, but your solution would also work.

Anybody building their SFML-based project as a dynamic lib or other type of runtime-loadable code will run the risk of encountering the same issue.

@JonnyPtn

Copy link
Copy Markdown
Contributor

Only if they are loading SFML's internal implementation classes at runtime by name right? And even then for it to be a problem they'd need to be doing something that has issues between the two mixed versions?

I agree the problem in the abstract is real, but is mostly hypothetical as it pertains to SFML, partly due to SFML code, and also how we expect users to use SFML.

I would be very interested to know if you have a real use case or reproduction which causes issues with SFML, other than just a warning in the log?

@jkeller51

Copy link
Copy Markdown
Contributor Author

Okay, I spent some time this morning doing some more research and working on an example. It turns out that @JonnyPtn is correct here about directly invoking class/function names in code. If two dynamic libraries define the same class/function name, by default they are tagged separately by dylib in the symbol table and so they can coexist without conflicting, with some caveats.

Caveat 1: -flat_namespace
If the executable is built with -flat_namespace, then there really is just a single symbol table where all dynamic library symbols are treated as global, which would allow library B's implementation to override library A's.

Caveat 2: String lookups
As @JonnyPtn mentioned, anywhere a class might be referenced by string names, including NSClassFromString, objc_getClass, NSCoding, nibs, storyboards, and other Interface Builder data, and Info.plist keys such as NSPrincipalClass and NSExtensionPrincipalClass. If any SF___ class is looked up in this way, there can be name conflicts between libraries.

Caveat 3: Pointer casts
If a pointer to a class with the same name somehow winds up crossing boundaries between libraries, it would be casted to the "wrong version" of that class and likely break.

This is less serious than Apple's docs led me to believe. I do think providing an easy ready-to-go solution is worthwhile, but if we want to simply document somewhere that objective-C symbol collisions can be avoided by redefining the SFML Obj-C class names with a custom prefix, I'd be okay with that too.

DAW plugin development is exploding in recent years, especially with people using AI to generate 80-100% of it, and I'm sure I won't be the last person to encounter this warning and try to fix it.

@JonnyPtn

Copy link
Copy Markdown
Contributor

I'm not sure we'd even want to document that as a solution to be honest, it's creating two distinct versions of the classes so would only fix things if the host process knows which plugin it wants to use the class from, which would seem a backwards way to develop a plugin system

I think really the correct solution if the host process needs to use SFML internals it should either require that all the plugins use a shared version it provides, or that they all statically link the same version.

@jkeller51

Copy link
Copy Markdown
Contributor Author

I'm not sure we'd even want to document that as a solution to be honest, it's creating two distinct versions of the classes so would only fix things if the host process knows which plugin it wants to use the class from, which would seem a backwards way to develop a plugin system

I think really the correct solution if the host process needs to use SFML internals it should either require that all the plugins use a shared version it provides, or that they all statically link the same version.

The two distinct version of the classes is exactly the point -- it guarantees that any code on top of SFML that might use obj-c string lookups or pointer casts will be safe. It's not about protecting the host process, it's about protecting the plugin code that uses SFML.

In the case of developing DAW plugins, the host process doesn't even know SFML exists. We are talking about Apple Logic/GarageBand, FL Studio, Steinberg Cubase, UAD Luna, Ableton Live, and so on. There are many different hosts for a plugin dev to support, and many different UI and audio plugin framework combinations that could be used to develop a plugin that works across all hosts.

One of the most common audio plugin development frameworks these days, JUCE, solves this by postfixing obj-C class names with random hex strings upon instantiation to guarantee that multiple plugins using JUCE can be using different versions and still use all the features of Objective-C without fear of symbol collisions. I'm not suggesting any such thing here, just a simple way to change the SFML obj-C class names at build time, or at the very least, a description of the issue and a way to solve it. It's not always the case that the developer has full control over the system they are working in.

@JonnyPtn

Copy link
Copy Markdown
Contributor

Do any of your plugins access SFML internal classes like that? or can you find any plugins which do?

In my opinion we shouldn't be documenting "solutions" to problems which only exist in specific use cases AND only when they are misusing SFML internals. I don't really have much more to add

@jkeller51

Copy link
Copy Markdown
Contributor Author

Do any of your plugins access SFML internal classes like that? or can you find any plugins which do?

Mine don't, and most plugins aren't open source so I have no idea how many people use this feature of objective-C and in what scenarios. Seems like other library devs wouldn't go through the trouble of hardening their libraries against it if it didn't matter.

All use cases end up being "specific", at the end of the day, and it seems like a slam dunk to broaden the horizon of supported use cases for an open source library such as this without breaking anything, as this PR achieves. But I'm curious what rule is being broken if a dev uses e.g. NSClassFromString in their code, in your opinion.

@JonnyPtn

Copy link
Copy Markdown
Contributor

Primarily that it's an internal implementation detail of SFML, not part of its API, much like we try to prevent users from accessing any other platform specific implementation classes, it's just harder (or impossible?) to prevent with the objective-c bits Apple requires

If there was any reason someone needed to access the internal classes directly then it would be good to know, as perhaps it indicates something lacking from the public API

@jkeller51

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants