如何使用泛型在 Swift 中实现 "C++-ish template specialization"?
How do I achieve "C++-ish template specialization" in Swift with generics?
我知道泛型和模板是不同的,但我想我应该放弃 C++-ish 的评论,因为熟悉模板的人会知道我想要完成什么。在下面的代码中,我试图编写一个通用成员方法来处理一个重载变体中的字符串。在另一个变体中,该方法应该处理可以从字符串初始化的数字类型。
所以,下面,我想拿一张地图,给定一个特定类型的变量,在地图中查找名称并将其解析为正确的类型。不幸的是,我遇到了关于 type(of:) 上缺少 init() 的问题以及 Int 类型调用想要调用为字符串定义的方法的重载解析的问题。
代码如下:
protocol StringInitializable {
init()
init( _: String )
}
class Foo {
var stringMember : String
var intMember : Int
var lookupMap : [String:String] = [
"string" : "Your String",
"int": "12"
]
func extractType< ParseEntity: StringInitializable >( parameter: ParseEntity, lookupName: String ) throws -> ParseEntity? {
var x : ParseEntity?
x = type( of: ParseEntity ).init( lookupMap[ lookupName ] )
return x
}
func extractType( parameter: String, lookupName: String ) throws -> String? {
return lookupMap[ lookupName ]
}
init() {
do {
try extractType( parameter: stringMember, lookupName: "string" )
try extractType( parameter: intMember, lookupName: "int")
} catch {}
}
}
代码有点乱,但希望足以传达其意图。任何帮助都表示赞赏。提前致谢。
我上面的公式有很多问题,但我最终得到了我想要的工作。
首先,在 StringInitializable 中,我需要以下内容:
protocol StringInitializable {
init()
init?(_ description: String)
}
其次,我必须通过协议扩展来扩展 Int 和 Float 以将它们标记为 "String Initializable":
extension Double : StringInitializable {}
extension Int : StringInitializable {}
第三,type(of:) 并不是 C++ 中 __decltype() 之类的纯粹替代品。我需要在 type(of:) 中构造一个 "ParseEntity"。所以在我重新编写的代码中,关键行看起来像这样:
var x : ParseEntity? = type( of: ParseEntity() ).init( lookupMap[ lookupName ]! )
所以最终的 "working" 示例如下所示:
enum ParseError : Error {
case BadParse( errorMessage: String)
}
protocol StringInitializable {
init()
init?(_ description: String)
}
extension Double : StringInitializable {}
extension Int : StringInitializable {}
class Foo {
var stringMember : String = ""
var intMember : Int = 0
var lookupMap : [String:String] = [
"string" : "Your String",
"int": "12"
]
func extractType< ParseEntity: StringInitializable >( parameter: ParseEntity, lookupName: String ) throws -> ParseEntity? {
if let lookupEntry = lookupMap[ lookupName ] {
return type( of: ParseEntity() ).init( lookupEntry )
}
throw ParseError.BadParse( errorMessage: "Bad parameter: \( lookupName )" )
}
func extractType( parameter: String, lookupName: String ) throws -> String? {
if let lookupEntry = lookupMap[ lookupName ] {
return lookupEntry
}
throw ParseError.BadParse( errorMessage: "Bad parameter: \( lookupName )" )
}
init() {
do {
stringMember = try extractType( parameter: stringMember, lookupName: "string" )!
intMember = try extractType( parameter: intMember, lookupName: "int")!
} catch ParseError.BadParse( let errorMessage ) {
print( "\( errorMessage )" )
} catch {}
}
}
var f = Foo()
print( "\(f.intMember)" )
print( "\(f.stringMember)" )
我知道泛型和模板是不同的,但我想我应该放弃 C++-ish 的评论,因为熟悉模板的人会知道我想要完成什么。在下面的代码中,我试图编写一个通用成员方法来处理一个重载变体中的字符串。在另一个变体中,该方法应该处理可以从字符串初始化的数字类型。
所以,下面,我想拿一张地图,给定一个特定类型的变量,在地图中查找名称并将其解析为正确的类型。不幸的是,我遇到了关于 type(of:) 上缺少 init() 的问题以及 Int 类型调用想要调用为字符串定义的方法的重载解析的问题。
代码如下:
protocol StringInitializable {
init()
init( _: String )
}
class Foo {
var stringMember : String
var intMember : Int
var lookupMap : [String:String] = [
"string" : "Your String",
"int": "12"
]
func extractType< ParseEntity: StringInitializable >( parameter: ParseEntity, lookupName: String ) throws -> ParseEntity? {
var x : ParseEntity?
x = type( of: ParseEntity ).init( lookupMap[ lookupName ] )
return x
}
func extractType( parameter: String, lookupName: String ) throws -> String? {
return lookupMap[ lookupName ]
}
init() {
do {
try extractType( parameter: stringMember, lookupName: "string" )
try extractType( parameter: intMember, lookupName: "int")
} catch {}
}
}
代码有点乱,但希望足以传达其意图。任何帮助都表示赞赏。提前致谢。
我上面的公式有很多问题,但我最终得到了我想要的工作。
首先,在 StringInitializable 中,我需要以下内容:
protocol StringInitializable {
init()
init?(_ description: String)
}
其次,我必须通过协议扩展来扩展 Int 和 Float 以将它们标记为 "String Initializable":
extension Double : StringInitializable {}
extension Int : StringInitializable {}
第三,type(of:) 并不是 C++ 中 __decltype() 之类的纯粹替代品。我需要在 type(of:) 中构造一个 "ParseEntity"。所以在我重新编写的代码中,关键行看起来像这样:
var x : ParseEntity? = type( of: ParseEntity() ).init( lookupMap[ lookupName ]! )
所以最终的 "working" 示例如下所示:
enum ParseError : Error {
case BadParse( errorMessage: String)
}
protocol StringInitializable {
init()
init?(_ description: String)
}
extension Double : StringInitializable {}
extension Int : StringInitializable {}
class Foo {
var stringMember : String = ""
var intMember : Int = 0
var lookupMap : [String:String] = [
"string" : "Your String",
"int": "12"
]
func extractType< ParseEntity: StringInitializable >( parameter: ParseEntity, lookupName: String ) throws -> ParseEntity? {
if let lookupEntry = lookupMap[ lookupName ] {
return type( of: ParseEntity() ).init( lookupEntry )
}
throw ParseError.BadParse( errorMessage: "Bad parameter: \( lookupName )" )
}
func extractType( parameter: String, lookupName: String ) throws -> String? {
if let lookupEntry = lookupMap[ lookupName ] {
return lookupEntry
}
throw ParseError.BadParse( errorMessage: "Bad parameter: \( lookupName )" )
}
init() {
do {
stringMember = try extractType( parameter: stringMember, lookupName: "string" )!
intMember = try extractType( parameter: intMember, lookupName: "int")!
} catch ParseError.BadParse( let errorMessage ) {
print( "\( errorMessage )" )
} catch {}
}
}
var f = Foo()
print( "\(f.intMember)" )
print( "\(f.stringMember)" )