Reputation: 361
I'm trying to represent a function call so that I can make a little scripting language for creating games. Right now I'm just trying to set up the interfaces between all the protocols and classes that I need. I have a class, FunctionCall<T>
. It has a method execute()
that executes the current function and returns an instance of type T?
. FunctionCall
also has a an array of instances of type FunctionCall
to represent any parameters. It also has a field, stringRepresentation
, which is a string representation of the function call, which the user will enter. This string might be something like createNode(named: myCircle)
or, in the base case, it might be just a literal, like myCircle
. To be clear, myCircle
in this case is a String
. I'm probably going to choose not to use quotation marks around String
s in my little scripting language.
My problem is with the execute()
method. I want to return an instance of type T
. So far, I've thought of enforcing that T
conforms to a protocol (let's call it Parseable
) that enforces that it has a method that takes a String
and returns an instance of type T
. The problem I found with that approach is that I have no way of creating such a method, because I don't have a way to reference the type that will be implementing the protocol from within the protocol. In other words, if T is SKShapeNode
, I have no way or referencing SKShapeNode
from within Parseable
, so that I can indicate that the return type must be an SKShapeNode
. The other approach I found is to make Parseable
have a required initializer that takes a String
. This works when structs implement the protocol, but not with classes. The problem I get when I try to implement the protocol in a class is that the class wants me to make the initializer required
, but I can't do that because I can't put a required
initializer in an extension.
I want the FunctionCall
class to look something like this
class FunctionCall<T: Parseable> {
var parameters = [FunctionCall]()
var stringRepresentation: String!
init(stringRepresentation: String) {
self.stringRepresentation = stringRepresentation
}
func execute() -> T? {
guard let indexOfFirstLeftParen = stringRepresentation.firstIndex(of: "("),
let indexOfLastRightParen = stringRepresentation.lastIndex(of: ")") else {
// then this is a literal value, because no function is being called
return T(string: stringRepresentation)
}
// TODO: implement
return nil
}
}
Upvotes: 2
Views: 91
Reputation: 272385
The problem I found with that approach is that I have no way of creating such a method, because I don't have a way to reference the type that will be implementing the protocol from within the protocol.
Actually, that problem can be solved by using Self
, which refers to whatever the conforming type is:
// I think this is a better name
protocol ConvertibleFromString {
static func from(string: String) -> Self?
}
// implementation
extension Int : ConvertibleFromString {
static func from(string: String) -> Int? {
return Int(string)
}
}
For non-final classes, you have to create a final subclass like this:
final class SKSpriteNodeFinal : SKSpriteNode, ConvertibleFromString {
static func from(string: String) -> SKSpriteNodeFinal? {
...
}
}
Note that this prevents your protocol from being used as a variable's type:
var foo: ConvertibleFromString? = nil // error
But I don't think that will be a problem, as you only use ConvertibleFromString
as generic constraints, which is OK.
Upvotes: 3