Last active
December 1, 2017 23:56
-
-
Save erica/e64d65adc7dab501607d75342565476a to your computer and use it in GitHub Desktop.
This file contains 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
import Dispatch | |
/* | |
Note: Thanks, Ian Keen and Zev Eisenberg and Sven Weidauer | |
Zev Eisenberg: "Do you still have to specify 1 << _n_ manually for `OptionSet` conformance? There’s no magic?" | |
This solution creates values that don't matter. They're simply unique, since option sets should not be accessed by raw value outside the implementation. (Versus direct `UInt`, which supports bit manipulation operations) | |
*/ | |
/// Stores `OptionSet` counts | |
/// | |
/// - Note: I dislike unparented globals like this | |
private var _optionSetDict: [AnyHashable: Int] = [:] | |
/// Prevents concurrent reads/writes of `_optionSetDict` | |
/// | |
/// - Note: I dislike unparented globals like this | |
private var _optionSetAccessQueue = DispatchQueue(label: "sadun.OptionSetGeneration", attributes: [.concurrent]) | |
/// Extends `OptionSet` to allow automatic construction | |
/// of option entries | |
/// | |
/// This approach: | |
/// | |
/// * avoids specific option numbers | |
/// * hides implementation details | |
/// * allows better type editing to add/remove options | |
/// | |
/// - Caution: the raw option value must not be meaningful | |
/// or accessed directly, as they may change during each | |
/// compilation/execution. | |
public extension OptionSet where RawValue == Int { | |
/// Returns an auto-generated option for an option set | |
/// using monotonically increasing bit offsets. | |
/// | |
/// - Caution: the raw option value must not be meaningful | |
/// or accessed directly, as they may change during each | |
/// compilation/execution. | |
public static func generateOption() -> Self { | |
// Thanks Sven Weidauer http://twitter.com/5sw_de | |
let key = ObjectIdentifier(Self.self) | |
return _optionSetAccessQueue.sync(flags: [.barrier]) { | |
// This should be locked so there's a guarantee that | |
// counts are unique for each generated option | |
let count = _optionSetDict[key, default: 0] | |
_optionSetDict[key] = count + 1 | |
return .init(rawValue: 1 << count) | |
} | |
} | |
} | |
// For example | |
public struct Traits: OptionSet { | |
public typealias RawValue = Int | |
public var rawValue = 0 | |
public init(rawValue: Traits.RawValue) { | |
self.rawValue = rawValue | |
} | |
public static let bolded = generateOption() | |
public static let italicized = generateOption() | |
public static let monospaced = generateOption() | |
public static let underlined = generateOption() | |
public static let outlined = generateOption() | |
} | |
// For example | |
var mySet: Traits = [] | |
print(mySet.rawValue) // 0 | |
mySet.formUnion([.bolded, .monospaced]) | |
print(mySet.rawValue) // 3 | |
mySet.formUnion([.italicized, .monospaced]) | |
print(mySet.rawValue) // 7 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment