Missing a warning on unexpected implicit enum casts - objective-c

I have made a new Objective-C project in Xcode 10.2.1 (Apple LLVM version 10.0.1 (clang-1001.0.46.4)), and have a code like this inside main.m:
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, Fruit) {
Apple,
Banana,
Coconut,
};
void boolArg(BOOL b) { if (b) puts("boolArg"); }
void shortArg(short s) { if (s > 0) puts("shortArg"); }
void fruitArg(Fruit f) { if (f == Banana) puts("fruitArg"); }
int main() {
Fruit f = Coconut;
// NEED WARN: passing Fruit instead of a boolean, unexpected cast
boolArg(f);
// NEED WARN: fruit (NSUInteger) instead of a short, unexpected cast losing precision
shortArg(f);
// NEED WARN: passing boolean instead of a Fruit, unexpected cast
fruitArg(YES);
return 0;
}
In my codebase I'd like to protect from such implicit casts. Is it possible to enable some clang warning to catch this?
What I tried so far and it didn't help:
enabled "Implicit Signedness Conversions" warning;
added to "Other Warnings Flags": -Wall, -Wextra;
change main.m to main.mm (to compile with Objective-C++) - only catches the last fruitArg call.

Related

Objective-C Closure to use Swift enum

I've got Swift enum like this:
#objc public enum Status: Int {
case unknown;
case ok;
case failed;
}
It's properly bridged to Objective-C, and I can use it as, say StatusUnknown in Objective-C.
Now I have a function with callback:
+ (void)fetch:(void (^_Nonnull)(BOOL success))completion
And all I want is to replace BOOL with my Status enum. How to do that?
Clearly not like this:
+ (void)fetch:(void (^_Nonnull)(Status success))completion // Error: Unknown type name
I could use NSInteger like this:
+ (void)fetch:(void (^_Nonnull)(NSInteger success))completion
but then it's not really limiting values to Status enum.
So what is the best way to convey enum here?
Note:
I simplified question, in reality enum is not called status and has many more values.
Signature of the function has to match previous signature, but with different argument
To be compatible with objective-c enum must be inherited from Int, like
#objc public enum Status: Int {
case unknown
case ok
case failed
}
make sure generated bridge header file "YOURPROJECT-Swift.h" contains
typedef SWIFT_ENUM(NSInteger, Status, closed) {
StatusUnknown = 0,
StatusOk = 1,
StatusFailed = 2,
};
then in your .m file
#import "YOURPROJECT-Swift.h"
...
+ (void)fetch:(void (^_Nonnull)(Status success))completion
{
// do anything needed
}
Clean/Build - all compiled well. Tested with Xcode 11.2.

Swift doesn't allow function overloading in objective-c or c headers

I have some swift code, which is not really important in context of question.
Also i have Objective-C bridging header ObjC-Swift-Bridging.h:
#import "my_objective_cpp_header.h"
And i have free C/C++ - style functions in that header:
inline void foo() {
}
inline void foo(int i) {
}
When i try to compile it in xcode 9.2 with enabled swift 4.0 i get error:
my_objective_cpp_header.h:29:13: error: redefinition of 'foo'
inline void foo(int i){
^
my_objective_cpp_header.h:26:13: note: previous definition is here
inline void foo() {
^
ObjC-Swift-Bridging.h:22:9: note: in file included from ObjC-Swift-Bridging.h:22:
#import "my_objective_cpp_header.h"
The same happens if i put these functions to C/C++ header and write
#include "my_cpp_header.h"
into ObjC-Swift-Bridging.h
So... Swift doesn't support code which was supported in Objective-C++? Did i miss something?

How to use ObjC block typedefs in Swift2 (esp. when containing BOOL parameters)

After moving on to Xcode7 (beta5) and Swift 2, I'm getting errors in my Swift code regarding Bool (or BOOL / ObjCBool) values that are passed from (or into) closures with an Objective-C typedef.
typedef void (^completion_success_block_t) (BOOL success);
When I use this type in my Swift class, I'm getting compiler errors.
func doSomething(completionBlock : completion_success_block_t) {
doSomethingElse { success in
if success == true { } // (1) error 1
let foo : Bool = true
completionBlock(foo) // (2) error 2
completionBlock(true) // (3) works just fine!
}
}
// error 1: "Binary operator '==' cannot be applied to operands of type 'ObjCBool' and 'Bool'"
// error 2: "Cannot invoke 'completionBlock' with an argument list of type '(Bool)'"
func doSomethingElse(completionBlock : completion_success_block_t) {
completionBlock(true)
}
Why does line (3) compile just fine, but not line (2)?
It seems like the compiler does convert between ObjCBools and Bools in some cases but not always.
It feels like using my ObjC typedef is like telling the compiler: 'I really want this to be an ObjCBool, not a Bool, so please don't do any conversions for me'. But that's not what I want. My code was working perfectly fine in Xcode 6.
Now I only see two options:
1) convert all values manually before passing / using them: let swiftSuccess = Bool(success) and let objCFoo = ObjCBool(foo)
2) stop using the ObjC typedefs for blocks containing BOOL parameters
Is there a better way? Perhaps changing the block signature in the typedef to work with both Swift and ObjC? But how?
Cool! Apple fixed this in Xcode Version 7.0 (7A218). Now passing a Swift Bool to a closure that was an ObjC typedef with a BOOL parameter works just fine. Comparing the BOOL parameter with true or false in Swift works as well.
Only downside for me: now all my workaround lines of code (e.g. completionBlock(ObjCBool(true))) cause compiler errors (sometimes even of the slightly obscure Segmentation fault: 11 type). Have to revert it all back. Sigh... (thank god there's git)

Type 'Boolean' does not conform to protocol 'BooleanType'

In attempting to create a Launch Helper as per the Apple docs (and tutorial-ized), I seem to be hitting a hiccup caused by porting the Objective-C code into Swift... who's compiler couldn't be any more redundant in this case.
import ServiceManagement
let launchDaemon: CFStringRef = "com.example.ApplicationLauncher"
if SMLoginItemSetEnabled(launchDaemon, true) // Error appears here
{
// ...
}
The error seems to consistently be:
Type 'Boolean' does not conform to protocol 'BooleanType'
I have tried casting to Bool in a number of locations, in case I'm simply dealing with a redundant, archaic primitive (either brought in by Obj-C or Core Foundation), to no avail.
Just in case, I have tried casting the response:
SMLoginItemSetEnabled(launchDaemon, true) as Bool
which yields the error:
'Boolean' is not convertible to 'Bool'
...seriously?
Boolean is a "historic Mac type" and declared as
typealias Boolean = UInt8
so this compiles:
if SMLoginItemSetEnabled(launchDaemon, Boolean(1)) != 0 { ... }
With the following extension methods for the Boolean type
(and I am not sure if this has been posted before, I cannot find it right now):
extension Boolean : BooleanLiteralConvertible {
public init(booleanLiteral value: Bool) {
self = value ? 1 : 0
}
}
extension Boolean : BooleanType {
public var boolValue : Bool {
return self != 0
}
}
you can just write
if SMLoginItemSetEnabled(launchDaemon, true) { ... }
The BooleanLiteralConvertible extension allows the automatic conversion of
the second argument true to Boolean.
The BooleanType extension allows the automatic conversion of the Boolean
return value of the function to Bool for the if-statement.
Update: As of Swift 2 / Xcode 7 beta 5, the "historic Mac type" Boolean
is mapped to Swift as Bool, which makes the above extension methods
obsolete.
Right, I had a similar issue trying to get the BOOL return of an objective-C method in Swift.
Obj-C:
- (BOOL)isLogging
{
return isLogging;
}
Swift:
if (self.isLogging().boolValue)
{
...
}
this was the way that I got rid of the error.

What header file needs to be included for using nullptr in g++?

I am using g++ 4.4.1 and want to use nullptr, but I am not being able to find which header file is required to be included. It does not seem to be keyword either, because my attempt to use it is rejected as
error: 'nullptr' was not declared in this scope
GCC 4.4.1 does not support nullptr.
Support for nullptr was added in GCC 4.6.0:
http://gcc.gnu.org/gcc-4.6/changes.html
Improved experimental support for the
upcoming C++0x ISO C++ standard,
including support for nullptr (thanks
to Magnus Fromreide), noexcept,
unrestricted unions, range-based for
loops (thanks to Rodrigo Rivas Costa),
implicitly deleted functions and
implicit move constructors.
For earlier versions of GCC, if you want to experiment with nullptr you can try the workaround in this SO question:
Can nullptr be emulated in GCC?
I would recommend not using nullptr as defined above, because it can be dangerous. If you want to use nullptr the following statement should be true.
sizeof(nullptr) == sizeof(void*) == sizeof(any pointer)
However, sizeof(nullptr) (as defined above) will not comply to this rule. It will actually evaluate to sizeof(bad nullptr) = 1.
This is a correct implementation.
#pragma once
namespace std
{
//based on SC22/WG21/N2431 = J16/07-0301
struct nullptr_t
{
template<typename any> operator any * () const
{
return 0;
}
template<class any, typename T> operator T any:: * () const
{
return 0;
}
#ifdef _MSC_VER
struct pad {};
pad __[sizeof(void*)/sizeof(pad)];
#else
char __[sizeof(void*)];
#endif
private:
// nullptr_t();// {}
// nullptr_t(const nullptr_t&);
// void operator = (const nullptr_t&);
void operator &() const;
template<typename any> void operator +(any) const
{
/*I Love MSVC 2005!*/
}
template<typename any> void operator -(any) const
{
/*I Love MSVC 2005!*/
}
};
static const nullptr_t __nullptr = {};
}
#ifndef nullptr
#define nullptr std::__nullptr
#endif
I use -std=c++0x to enable the nullptr feature with gcc 4.6.3
If you don't have the latest gcc which supports C++11 , try using NULL instead of nullptr.