Last active
June 5, 2024 20:44
-
-
Save dabrahams/b3413352d9765eeaa932d19e6fd2787b to your computer and use it in GitHub Desktop.
Class factory initializers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Classes whose initializers actually create derived classes | |
protocol FactoryInitializable { | |
/// The type of the least-derived class declared to be FactoryInitializable. | |
/// | |
/// - Warning: Do not define this in your FactoryInitializable type! | |
associatedtype FactoryBase: AnyObject, FactoryInitializable = Self | |
// This associatedtype is a trick that captures `Self` at the point where | |
// `FactoryInitializable` enters a class hierarchy; in other contexts, `Self` | |
// refers to the most-derived type. | |
} | |
extension FactoryInitializable where Self: AnyObject { | |
/// Optimally “creates” an instance that is just another reference to `me`. | |
/// | |
/// - Requires: `me is Self`. | |
/// | |
/// Taking `FactoryBase` as a parameter prevents, at compile-time, the | |
/// category of bugs where `me` is not derived from the least-derived ancestor | |
/// of `Self` conforming to `FactoryInitializable`. | |
/// | |
/// However, there are still ways `me` might not be derived from `Self`. If | |
/// you have factory initializers at more than one level of your class | |
/// hierarchy and you can't control exactly what is passed here, use | |
/// `init(aliasing:)` instead. | |
init(unsafelyAliasing me: FactoryBase) { | |
self = unsafeDowncast(me, to: Self.self) | |
} | |
/// Safely “creates” an instance that is just another reference to `me`. | |
/// | |
/// - Requires: `me is Self`. | |
init(aliasing me: FactoryBase) { | |
self = me as! Self | |
} | |
} | |
public class Base : FactoryInitializable { | |
/// Constructs an instance whose dynamic type depends on the value of `one` | |
public convenience init(_ one: Bool) { | |
self.init(unsafelyAliasing: one ? Derived1() : Derived2()) | |
} | |
} | |
internal class Derived1 : Base { | |
override public init() { super.init() } | |
} | |
internal class Derived2 : Base { | |
override public init() { super.init() } | |
} | |
// Inspect the codegen for testMe1 and testMe2 to see that we are getting | |
// optimal results. | |
@inline(never) | |
func testMe1(_ one: Bool) -> Base { | |
Base(one) | |
} | |
@inline(never) | |
func testMe2(_ one: Bool) -> Base { | |
one ? Derived1() : Derived2() | |
} | |
print(testMe1(true)) | |
print(testMe2(true)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment