Skip to content

Instantly share code, notes, and snippets.

@erica
Last active December 1, 2017 23:56
Show Gist options
  • Save erica/e64d65adc7dab501607d75342565476a to your computer and use it in GitHub Desktop.
Save erica/e64d65adc7dab501607d75342565476a to your computer and use it in GitHub Desktop.
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