This came up in msgpack. The library have a registry of serializers on a per class basis, and wish to support singleton classes too.
So it is calling rb_singleton_class() which cause lots of useless singleton classes to be created: https://github.com/msgpack/msgpack-ruby/pull/245
!SPECIAL_CONST(obj) && FL_TEST(RBASIC(obj)->klass, FL_SINGLETON);
Which does seem very clean.
Beyond cleanliness, it uses FL_SINGLETON, which is marked@internal. I
agree with @shyouhei (Shyouhei Urabe) that this flag should not be visible in the first place.
Also, it's tempting to think the predicate being true implies RBASIC(obj)->klass gives the singleton class of obj while that is not
necessarily true and has lead to bugs in the past. 12 These subtleties
are why it's generally a bad idea for extensions to take advantage of
implementation details.
I can emphasize with trying to avoid allocations, but I want to be cautious
about expanding the C extension API surface. In this case, it's exposing yet another operation unavailable to plain Ruby code. It might make some
optimizations in JITs more complex to implement or worse, introduce bugs down
the line in the interpreter because C APIs are generally less frequently
exercised and their extra capabilities easy to be forgotten.
We should review how much rb_singleton_class_get() is exposing and not exposing, and
whether it's feasible to implement in other runtimes such as TruffleRuby.
In this case, it's exposing yet another operation unavailable to plain Ruby code.
To be fair, I was entertaining the idea to also open a feature request to get a Ruby side API for it, e.g. Object#has_singletong_class?.
We should review how much rb_singleton_class_get() is exposing and not exposing
It really doesn't expose much, it's nearly the same code than rb_singleton_class except it returns Qnil instead of creating the singleton class on the fly.
whether it's feasible to implement in other runtimes such as TruffleRuby.
Not sure about TruffleRuby, but since msgpack has a JRuby extension, I also had to do this same there, and their API already allows to check for the singleton class existence without creating it.
A new function seems better than that flag check (which doesn't work on TruffleRuby currently, will always return false).
The name rb_singleton_class_get() doesn't seem great though.
However I think there is already a way to check this, RBASIC_CLASS(obj)/rb_class_of(obj) already gets the singleton_class-if-there-is-one, or the same as .class, and there is Module#singleton_class? to find out.
The first is only available to C exts, the second doesn't seem to have a C API function.
But, this is an easy way in C: