Wrong argument type for NSAssert1 - objective-c

This isn't giving me an error but several warnings I'd like to get rid of.
OSErr err = AudioUnitInitialize(toneUnit);
NSAssert1(err == noErr, #"Error initializing unit: %ld", err);
The warning I'm getting is: Format specifies type 'long' but the argument has type 'OSErr' (aka 'short'). Obviously I can change this by using long instead of OSErr but I was wondering why I'm getting the error. This is an upcast so I thought it shouldn't cause any problems.
Can anyone explain?

Since NSAssert1 is a variadic function implemented within the constraints of C, it cannot perform the cast internally: it is not possible to find the type of an argument passed in the vararg section of the function. Objective-C compiler will perform the default type promotions before making the call, but the short is converted to int, not to long by default. That is why you should either insert an explicit cast, or use a different format specifier.

Use
NSAssert1(err == noErr, #"Error initializing unit: %hd", err);
or
NSAssert1(err == noErr, #"Error initializing unit: %ld", (long)err);
instead.

Related

What is the rationale behind Apple's pattern of checking return value rather than error?

Apple's guide on Using and Creating Error Objects gives the following code example:
NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:#"html" error:&theError];
if (success == NO) {
// Maybe try to determine cause of error and recover first.
NSAlert *theAlert = [NSAlert alertWithError:theError];
[theAlert runModal]; // Ignore return value.
}
and accompanies it with the statement:
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
I have always been wondering why is this pattern so important? Why should we ALWAYS check the return value? What's wrong if we check whether the error is nil or not?
This design is not terribly unusual, compare also errno in standard C.
The design has a number of potential advantages:
The function does not have to write through a pointer on success. This does not only make the implementation of such functions easier and less error prone, it can also be a small performance advantage (e.g. this prevent CPU caches from being invalidated if the function succeeds).
If we always check that the function failed before accessing an error, we can use that same error pointer for multiple functions. Otherwise, we might get a previous failure rather than the failure of the most recent function.
This makes validation code easier to write. E.g. a function could set the error by default. If all validations pass, the function can simply return success instead of having to reset the error variable.
A function can use the same error pointer when calling other functions, but a failure of these helpers doesn't necessarily imply a failure of the top function.
In your specific case, the variable NSError *theError; has not been initialized. Accessing that variable without assigning to it first would invoke undefined behaviour. The documentation only guarantees that the variable will be set in case of an error.
Imagine you implement a method in terms of a few other methods:
-(BOOL)sendCachedRequestReturningError: (NSError**)err {
BOOL success = [self readCachedRequestReturningError:err];
if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) {
success = [self sendUncachedRequestReturningError:err];
}
return success;
}
Now there are 4 code paths here:
There is a cached request. We'll just return success == YES and all is good.
A non-recoverable error occurs trying to read from the cache. readCachedRequestReturningError: will set err and set success == NO and the caller will call presentError: or whatever
An error occurs trying to do the network request. Same as #2, err is set, and success == NO.
There is no cache, but we can make a network request. readCachedRequestReturningError: will set err to a valid NSError{MYFileDomain, MYFileNotFoundCode}, but then sendUncachedRequestReturningError: will succeed and set success == YES, and not touch err at all, leaving the previous error in it. If you now check err instead of checking the return value, you will think there was an error when all went well.
Note: The code above is grossly simplified, because we only care about errors. Of course in a real program, the methods would probably have another return parameter for the actual reply from the request, or would return the reply or nil instead of a success BOOL. It would also probably check whether err is NULL.

Why can't the Option.expect() message be downcast as a &'static str when a panic is handled with catch_unwind?

I have the following code:
use std::thread;
use std::panic;
pub fn main(){
thread::spawn(move || {
panic::catch_unwind(|| {
// panic!("Oh no! A horrible error.");
let s: Option<u32> = None;
s.expect("Nothing was there!");
})
})
.join()
.and_then(|result| {
match result {
Ok(ref val) => {
println!("No problems. Result was: {:?}", val);
}
Err(ref err) => {
if let Some(err) = err.downcast_ref::<&'static str>() {
println!("Error: {}", err);
} else {
println!("Unknown error type: {:?}", err);
}
}
}
result
});
}
When I trigger a panic! directly (by uncommenting the line in the code above), then I get an output which includes my error message:
Error: Oh no! A horrible error.
But, if I use Option::expect(&str), as above, then the message cannot be downcast to &'static str, so I can't get the error message out:
Unknown error type: Any
How can I get the error message, and how would I find the correct type to downcast to in the general case?
Option::expect expects a message as a &str, i.e. a string slice with any lifetime. You can't coerce a &str to a &'static str, as the string slice may refer to the interior of a String or Box<str> that could be freed at any time. If you were to keep a copy of the &'static str around, you would be able to use it after the String or Box<str> has been dropped, and that would be undefined behavior.
An importail detail is that the Any trait cannot hold any lifetime information (hence the 'static bound), as lifetimes in Rust are erased at compile time. Lifetimes are used by the compiler to validate your program, but a program cannot distinguish a &'a str from a &'b str from a &'static str at runtime.
[...] how would I find the correct type to downcast to in the general case?
Unfortunately, it's not easy. Any has a method (unstable as of Rust 1.15.1) named get_type_id that lets you obtain the TypeId of the concrete object referred to by the Any. That still doesn't tell you explicitly what type that is, as you still have to figure out which type this TypeId belongs to. You would have to get the TypeId of many types (using TypeId::of) and see if it matches the one you got from the Any, but you could do the same with downcast_ref.
In this instance, it turns out that the Any is a String. Perhaps Option::expect could eventually be specialized such that it panics with the string slice if its lifetime is 'static and only allocates a String if it's not 'static.
Like Francis said, you can't in general discover and cast to the type of a panic. However, that being said, panics have the following rules:
If you panic! with a single argument, the panic will have that type. Typically this is &'static str.
If you panic! with more than one argument, the arguments will be treated as format! parameters and used to create a String argument.
These rules are documented in the panic documentation: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html.
With these rules in mind, we can write a function to extract the message from a panic in any case where there is a message available to be extracted, which in practice works most of the time, because most of the time the message is either &'static str or String:
pub fn get_panic_message(panic: &Box<dyn Any + Send>) -> Option<&str> {
panic
// Try to convert it to a String, then turn that into a str
.downcast_ref::<String>()
.map(String::as_str)
// If that fails, try to turn it into a &'static str
.or_else(|| panic.downcast_ref::<&'static str>().map(Deref::deref))
}
I use this exact function in an assertions library I wrote a while ago; you can see some examples of its use in the relevant test suite.

MKPolygon using Swift (Missing argument for parameter 'interiorPolygons' in call)

Fellow Devs,
I'm trying to implement a polygon overlay on a mapview as follows:
private func drawOverlayForObject(object: MyStruct) {
if let coordinates: [CLLocationCoordinate2D] = object.geometry?.coordinates {
let polygon = MKPolygon(coordinates: coordinates, count: coordinates.count)
self.mapView.addOverlay(polygon)
}
}
The following error is presented:
Missing argument for parameter 'interiorPolygons' in call
According to the documentation:
Apple Docu:
Mutable Pointers
When a function is declared as taking an UnsafeMutablePointer
argument, it can accept any of the following:
nil, which is passed as a null pointer
An UnsafeMutablePointer value
An in-out expression whose operand is a stored lvalue of type Type, which is passed as the address of the lvalue
An in-out [Type] value, which is passed as a pointer to the start of the array, and lifetime-extended for the duration of the call
Now I think that my approach then would be correct, providing a [CLLocationCoordinate2D] array. Did anyone experience the same problem and found a workaround?
thanks
Ronny
The error you're getting is Swift's cryptic way of saying that it can't find a method which matches your parameters. If you did try passing the interiorPolygons parameter, you'd get an equally confusing:
Extra argument 'interiorPolygons' in call
Your code is pretty close though; you just need a couple of minor changes. In the doc you reference, it says one of the things you can pass is:
An in-out [Type] value, which is passed as a pointer to the start of
the array, and lifetime-extended for the duration of the call
So, it's looking for an in-out parameter. Which is done by passing coordinates prefixed with an &, like so:
MKPolygon(coordinates: &coordinates, count: coordinates.count)
But, in-out parameters can't be constants. From the docs:
You can only pass a variable as the argument for an in-out parameter.
You cannot pass a constant or a literal value as the argument, because
constants and literals cannot be modified.
So, you need to define coordinates with a var first:
if var coordinates: [CLLocationCoordinate2D] = object.geometry?.coordinates
Which makes the entire function look like this:
private func drawOverlayForObject(object: MyStruct) {
if var coordinates: [CLLocationCoordinate2D] = object.geometry?.coordinates {
let polygon = MKPolygon(coordinates: &coordinates, count: coordinates.count)
self.mapView.addOverlay(polygon)
}
}
My final solution by cherry-picking from several tutorials and integrating:
func setPolylineFromPoints(locations:[CLLocation]){
if locations.count == 0 {
return;
}
// while we create the route points, we will also be calculating the bounding box of our route
// so we can easily zoom in on it.
var pt : UnsafeMutablePointer<MKMapPoint>? // Optional
pt = UnsafeMutablePointer.alloc(locations.count)
for idx in 0..<locations.count-1 {
let location = locations[idx]
let point = MKMapPointForCoordinate(location.coordinate);
pt![idx] = point;
}
self.polyline = MKPolyline(points:pt!, count:locations.count-1)
// clear the memory allocated earlier for the points
pt?.destroy()
pt?.dealloc(locations.count)
}

Saving enumerated values to a database

I'm new to Go and I'm trying to write a little program to save enumerated values to a database.
The way I declare my values is as follows:
type FileType int64
const (
movie FileType = iota
music
book
etc
)
I use these values in my struct like this:
type File struct {
Name string
Type FileType
Size int64
}
I use gorp for my database stuff, but I guess the use of gorp isn't relevant to my problem. I put stuff in my DB like this:
dbmap.Insert(&File{"MyBook.pdf",movie,1000})
but when I try to retrieve stuff…
dbmap.Select(&dbFiles, "select * from Files")
I get the following error:
panic: reflect.Set: value of type int64 is not assignable to type main.FileType
When I use int64 as the type for the const(...) and for the File.Type field, everything works fine, but I'm new to Go and want to understand the problem.
The way I see it, I have two problems:
Why can't Go convert this stuff successfully? I looked at the source code of the Go reflection and sql packages and there are methods for this kind of conversion, but they seem to fail. Is this a bug? What is the problem?
I figured out, that one can implement the sql.Scanner interface by implementing the following method:
Scan(src interface{}) error
I tried to implement the method and I even was able to get the right value from src and convert it to a FileType, but I was confused if I should implement the method for "(f *FileType) or (f FileType). Either way the method gets invoked, however I'm not able to overwrite f (or at least the update gets lost later) and the File instances read from the DB always had a "0" as value for File.Type.
Do you have any ideas on those two points?
I recently had the same need, and the solution is to implement two interfaces:
sql/driver.Valuer
sql.Scanner
Here's a working example:
type FileType int64
func (u *FileType) Scan(value interface{}) error { *u = FileType(value.(int64)); return nil }
func (u FileType) Value() (driver.Value, error) { return int64(u), nil }
Slightly off-topic, but may be useful to others as I kept revisiting this question/answer when solving a similar problem when working with postgres enum fields in golang (which are returned as bytes).
// Status values
const (
incomplete Status = "incomplete"
complete Status = "complete"
reject Status = "reject"
)
type Status string
func (s *Status) Scan(value interface{}) error {
asBytes, ok := value.([]byte)
if !ok {
return errors.New("Scan source is not []byte")
}
*s = Status(string(asBytes))
return nil
}
func (s SubjectStatus) Value() (driver.Value, error) {
// validation would go here
return string(s), nil
}
Go needs to be specific with types, which can be a pain sometimes.
(f FileType) is cheaper than (f *FileType) for "native" types, pretty much unless you have a complex type, it's almost always better to not use a pointer.
What do you mean it doesn't overwrite it? did you resave the struct after you modified it?

Sending isEqual: to nil always returns NO

If you send isEqual: to an object that happens to be nil, you always get NO back.
Is this the expected behavior? To be a feature instead of a bug, I would expect it to return YES if the other object is also nil, and NO otherwise? Semantically this seems the correct behavior.
In case my expectations are incorrect, what the recommended proceedure? Check for nil before sending isEqual: (and friends)?
Yes, this is the expected behavior. Any message to nil will return a result which is the equivalent to 0 for the type requested. Since the 0 for a boolean is NO, that is the result.
This is expected behaviour from Objective-C. This basically means that doing this
if ([nil isEqual:nil]) { ... }
evaluates to NO. Even though it doesn't make sense, when looking at it - and even though it's annoying - being able to send messages to nil is actually one of the really cool things about Objective-C. Saves you a lot of code sometimes.
My solution is to define this macro somewhere handy
#define IsEqual(x,y) ((x && [x isEqual:y]) || (!x && !y))
So when I need to test if two objects are equal:
if (IsEqual(obj1, obj2)) { ... }
or not equal:
if (!IsEqual(obj1, obj2)) { ... }
Hope this helps.
It is expected, for two reasons: (1) in Objective-C, sending a message to nil always returns a false-y value (nil, NO, 0, 0.0, etc.; or, more generally speaking, 0, which can be interpreted based on the expected return type of the method); (2) nil represents an unknown value, and two unknown values are not necessarily equal to each other.
If you want to see if an object is nil, use if (!obj) or if (obj == nil).