swift - Is there a way to use a Template, and In-Out Parameter and Optional together? -
i ran interesting behaviour other day. wrote helper function prevent errors while automatically typecasting json property. looked this:
func readdata<t>(inout output:t, _ input:anyobject?, _ throwerror:bool = true) throws { if (input == nil) { if (throwerror) { throw converterror.missingparameter } } else { if let inputobject:t = input as? t { output = inputobject } else if (throwerror) { throw converterror.wrongtype } } } var myproperty:string try readdata(&myproperty, myjson["data"], true)
this checks property exists, , it's right type. if goes well, value inside myproperty changes.
a little while later, needed make changes. made class called properties , has list of properties inside of it. there 2 variables of type of class: originalproperties , modifiedproperties each of properties inside these classes optional variables now, keep track of properties user has changed. looks this:
class properties { var x:int? var y:int? }
now when run this:
try readdata(&originalproperties.x, myjson["x"], false)
it doesn't work anymore. looked @ another question , explained happening. while x still had value of nil (because it's optional), passing nil value readdata function, template type isn't set , that's why fails on input as? t code.
fortunately, don't have have such creative function anymore. can use this:
originalproperties.x= obj["x"] as? int
but i'll lose thrown error functionality if needed it.
does have ideas how can make sure template type gets passed correctly while still has value of nil? read in thread might have use sort of default value closure, interesting see if there's way work around this.
the main problem here generic t
can never itself know if of optional
type or not, makes successful type conversion t
tricky cases when t
in fact of type optional<sometype>
. could, ourselves, assert t
optional type (checking mirror(reflecting: ...).displaystyle == .optional
etc), still doesn't solve conversion t
right off bat. instead, use approach, follows below.
we can work around problem creating 2 readdata(...)
functions, 1 taking optional generic inout
parameter, type u?
, , other 1 taking implicitly non-optional generic inout
parameter u
(called if u?
function cannot used, hence implicitly called non-optionals). these 2 functions, in turn, minimal , calls "core" datareader(..)
function, we've made adjustment inout
generic parameter explicitly optional, i.e., t?
.
enum converterror: errortype { case missingparameter case wrongtype } /* optional inout parameter */ func readdata<u>(inout output: u?, _ input: anyobject?, _ throwerror: bool = true) throws { try readdatacore(&output, input, throwerror) } /* non-optional inout parameter */ func readdata<u>(inout output: u, _ input: anyobject?, _ throwerror: bool = true) throws { var outputopt : u? = output try readdatacore(&outputopt, input, throwerror) output = outputopt! /* use guard-throw here unwrapping of 'outputopt', note 'outputopt' initialized non-nil value, , can never become 'nil' in readdatahelper; "safe" forced unwrapping here. */ } /* "core" function */ func readdatacore<t>(inout output: t?, _ input: anyobject?, _ throwerror: bool = true) throws { if (input == nil) { if (throwerror) { throw converterror.missingparameter } } else { if let inputobject: t = input as? t { output = inputobject } else if (throwerror) { throw converterror.wrongtype } } }
as try out, see behaviour we're looking for, if argument sent inout
parameter nil
or optional
.
example 1: using optional inout
param value nil
class properties { var x:int? var y:int? } var myjson : [string:int] = ["data":10] var originalproperties = properties() { try readdata(&originalproperties.x, myjson["data"], true) print("originalproperties.x = \(originalproperties.x ?? 0)") } catch converterror.missingparameter { print("missing parameter") } catch converterror.wrongtype { print("wrong type") } catch { print("unknown error") } /* prints: 'originalproperties.x = 10', ok! */ // try non-existing key 'foo' { try readdata(&originalproperties.x, myjson["foo"], true) print("originalproperties.x = \(originalproperties.x ?? 0)") } catch converterror.missingparameter { print("missing parameter") } catch converterror.wrongtype { print("wrong type") } catch { print("unknown error") } /* prints: 'missing parameter', ok! */
example 2: using optional inout
param with, however, non-optional value
class properties { var x:int? = 1 var y:int? = 1 } var myjson : [string:int] = ["data":10] var originalproperties = properties() // ... /* same results example 1: 'originalproperties.x = 10' , 'missing parameter' */
example 3: using non-optional inout
parameter
class properties { var x:int = 1 var y:int = 1 } var myjson : [string:int] = ["data":10] var originalproperties = properties() // ... /* same results example 1: 'originalproperties.x = 10' , 'missing parameter' */
Comments
Post a Comment