Concatenating & storing music symbols - Objective-C - objective-c

I was using these unicode definitions for sharp and flat symbols and they work fine in string concats:
#define kSharpSymbol [NSString stringWithFormat:#"\U0000266F"]
#define kFlatSymbol [NSString stringWithFormat:#"\U0000266D"]
[...]
// Set F#
[f setNoteLetterName:[NSString stringWithFormat:#"F%#",kSharpSymbol]];
Then, I just read on a SO question that relying on the unicode formatting is not recommended by Apple so I went to this, which also works but results in compiler warnings when I do the implicit string concat:
Format specifies type 'unsigned short' but the argument has type 'int'
#define kSharpSymbol [NSString stringWithFormat:#"%C", 0x266F]
#define kFlatSymbol [NSString stringWithFormat:#"%C", 0x266D]
[...]
// Set F#
[f setNoteLetterName:[NSString stringWithFormat:#"F%#",kSharpSymbol]];
I guess I need some clarity on this. What's best and how do I get the compiler to be happy?

I would suggest another way to approach this problem: there is absolutely nothing wrong with using string constants that contain Unicode symbols directly, for example
#define kSharpSymbol #"♯"
#define kFlatSymbol #"♭"
The advantage is that the human readers of your program are going to see the symbol without looking it up in a table. The disadvantage is that the program is not going to look correctly when viewed in some older text editors that do not support modern file encoding. Fortunately, Xcode's editor is not one of them, so it shouldn't be a concern.

Related

How to convert c++ std::u32string to NSString?

I am developing a bridge between C++ and swift. And I need to convert C++ std::u32string to NSString. Here the code I tried:
u32string str = U"some string";
NSLog(#"%#", [[NSString alloc] initWithBytes:str.data() length:str.size() * sizeof(char32_t) encoding:NSUTF32StringEncoding]);
However initWithBytes returns nil.
Don't ask me why, but it seems to work if:
I insert a byte order mark (BOM) at the start of the string:u32string str = U"\uFEFFsome string";
or I use NSUTF32LittleEndianStringEncoding instead of NSUTF32StringEncoding.
Inserting byte order marks all over the place isn't terribly practical, so I guess you need to define your own constant which evaluates to either the little or big endian versions of the encoding constant depending on the platform being compiled.
This appears to be a quirk in Foundation, nothing specific to Objective-C++ or your use of std::u32string.

How to remove 100s of warnings "implicit conversion loses integer precision: 'NSInteger' (aka 'long') to 'int'" I got after updating to arm64?

Problem:
Yesterday I converted a large project of mine to support arm64 and after that I got 500+ warnings at once. About 70% of them are where NSInteger is being assigned to int or vice versa, and remaining are where NSUInteger is formatted in NSString like this:
NSInteger a = 123;
NSString *str = [NSString stringWithFormat:#"Int:%d", a]; //warning: value of 'NSInteger' should not be used as formate argument; add an explicit cast to 'unsigned long' instead.
Now I do know how to adress them manually, but that's a huge task and very laborious.
I'm also aware that I can silence the type mismatch warnings all together, but I don't want to do that. Of course, they're very helpful.
What I've tried:
I've converted [NSNumber numberWithInt:abc]; to [NSNumber numberWithInt:(int)abc]; using find-n-replace. It fixed some.
I've also tried to change all my int properties to NSInteger properties
but it doubled the number of warnings (reached to 900+ count). So I
reverted.
I've also tried to find some regular expression but couldn't find
something suitable to my needs.
Question:
I'm looking for a regular expression or any other workaround somebody has tried which can reduce the amount of work needed to fix them manually.
Thanks in advance.
NSInteger a = 123;
NSString *str = [NSString stringWithFormat:#"Int:%ld", (long)a];
After updating to 64 bit need to do typecast like this((long)a). %d is only for 32 bit range %ld for long integer. For better understanding got through this apple documentation.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Cocoa64BitGuide/ConvertingExistingApp/ConvertingExistingApp.html
In case someone else's facing a similar situation, I want to clarify how to deal with it. Although #Raju's answer is suggesting to do it manually (which I wanted to avoid), I found exactly what I needed at the link he shared.
Apple has provided a script for 64bit conversion called ConvertCocoa64, located at/Developer/Extras/64BitConversion/ConvertCocoa64 which not only converts all int to NSInteger it also deals with float to CGFloat conversion, as stated:
It converts most instances of int and unsigned int to NSInteger and
NSUInteger, respectively. It doesn't convert ints in bit-field
declarations and other inappropriate cases. During processing, the
script refers to a hardcoded list of exceptions.
In addition to above conversions it also flags the lines in code which need manual fix. So this might help with the warnings of String Formats.
Please refer to this link for complete details. It not only explains how to use the script but also suggests some very important post 64-bit migration check points.
objective c implicit conversion loses integer precision 'NSUInteger' (aka 'unsigned long') to 'int
Change key in Project > Build Setting "implicit conversion to 32Bits Type > Debug > *64 architecture : No"
Other warning
Change key in Project > Build Setting "typecheck calls to printf/scanf : NO"
Explanation : [How it works]
Check calls to printf and scanf, etc., to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense.
Hope it work
[caution: It may void other warning of 64 Bits architecture conversion].

When to use static string vs. #define

I am a little confused as to when it's best to use:
static NSString *AppQuitGracefullyKey = #"AppQuitGracefully";
instead of
#define AppQuitGracefullyKey #"AppQuitGracefully"
I've seen questions like this for C or C++, and I think what's different here is that this is specifically for Objective C, utilizing an object, and on a device like the iPhone, there may be stack, code space or memory issues that I don't yet grasp.
One usage would be:
appQuitGracefully = [[NSUserDefaults standardUserDefaults] integerForKey: AppQuitGracefullyKey];
Or it is just a matter of style?
Thanks.
If you use a static, the compiler will embed exactly one copy of the string in your binary and just pass pointers to that string around, resulting in more compact binaries. If you use a #define, there will be a separate copy of the string stored in the source on each use. Constant string coalescing will handle many of the dups but you're making the linker work harder for no reason.
See "static const" vs "#define" vs "enum". The main advantage of static is type safety.
Other than that, the #define approach introduces a flexibility of inline string concatenation which cannot be done with static variables, e.g.
#define ROOT_PATH #"/System/Library/Frameworks"
[[NSBundle bundleWithPath:ROOT_PATH#"/UIKit.framework"] load];
but this is probably not a good style :).
I actually would recommend neither, you should use extern instead. Objective-c already defines FOUNDATION_EXPORT which is more portable than extern, so a global NSString instance would look something like this:
.h
FOUNDATION_EXPORT NSString * const AppQuitGracefullyKey;
.m
NSString * const AppQuitGracefullyKey = #"AppQuitGracefully";
I usually put these in declaration files (such as MyProjectDecl.h) and import whenever I need.
There are a few differences to these approaches:
#define has several downsides, such as not being type safe. It is true that there are workarounds for that (such as #define ((int)1)) but what's the point? And besides, there are debugging disadvantages to that approach. Compilers prefer constants. See this discussion.
static globals are visible in the file they are declared.
extern makes the variable visible to all files. That contrasts with static.
Static and extern differ in visibility. It's also notable that neither of these approaches duplicates the string (not even #define) as the compiler uses String Interning to prevent that. In this NSHipster post they show proof:
NSString *a = #"Hello";
NSString *b = #"Hello";
BOOL wtf = (a == b); // YES
The operator == returns YES only if the two variables point at the same instance. And as you can see, it does.
The conclusion is: use FOUNDATION_EXPORT for global constants. It's debug friendly and will be visible allover your project.
After doing some search (this question/answer among other things) I think it is important to say that anytime when you are using string literal #"AppQuitGracefully" constant string is created, and no matter how many times you use it it will point to the same object.
So I think (and I apologize me if I'm wrong) that this sentence in above answer is wrong: If you use a #define, there will be a separate copy of the string stored in the source on each use.
I use static when I need to export NSString symbols from a library or a framework. I use #define when I need a string in many places that I can change easily. Anyway, the compiler and the linker will take care of optimizations.
USING #define :
you can't debug the value of identifier
work with #define and other macros is a job of Pre-Processor,
When you hit Build/Run first it will preprocess the source code, it will work with all the macros(starting with symbol #),
Suppose, you have created,
#define LanguageTypeEnglish #"en"
and used this at 2 places in your code.
NSString *language = LanguageTypeEnglish;
NSString *languageCode = LanguageTypeEnglish;
it will replace "LanguageTypeEnglish" with #"en", at all places.
So 2 copies of #"en" will be generated.
i.e
NSString *language = #"en";
NSString *languageCode = #"en";
Remember, till this process, compiler is not in picture.
After preprocessing all the macros, complier comes in picture, and it will get input code like this,
NSString *language = #"en";
NSString *languageCode = #"en";
and compile it.
USING static :
it respects scope and is type-safe.
you can debug the value of identifier
During compilation process if compiler found,
static NSString *LanguageTypeRussian = #"ru";
then it will check if the variable with the same name stored previously,
if yes, it will only pass the pointer of that variable,
if no, it will create that variable and pass it's pointer, next time onwards it will only pass the pointer of the same.
So using static, only one copy of variable is generated within the scope.

Objective c doesn't like my unichars?

Xcode complaints about "multi-character character contant"'s when I try to do the following:
static unichar accent characters[] = { 'ā', 'á', 'ă', 'à' };
How do you make an array of characters, when not all of them are ascii? The following works just fine
static unichar accent[] = { 'a', 'b', 'c' };
Workaround
The closest work around I have found is to convert the special characters into hex, ie this works:
static unichar accent characters[] = { 0x0100, 0x0101, 0x0102 };
It's not that Objective-C doesn't like it, it's that C doesn't. The constant 'c' is for char which has 1 byte, not unichar which has 2 bytes. (see the note below for a bit more detail.)
There's no perfectly supported way to represent a unichar constant. You can use
char* s="ü";
in a UTF-8-encoded source file to get the unicode C-string, or
NSString* s=#"ü";
in a UTF-8 encoded source file to get an NSString. (This was not possible before 10.5. It's OK for iPhone.)
NSString itself is conceptually encoding-neutral; but if you want, you can get the unicode character by using -characterAtIndex:.
Finally two comments:
If you just want to remove accents from the string, you can just use the method like this, without writing the table yourself:
-(NSString*)stringWithoutAccentsFromString:(NSString*)s
{
if (!s) return nil;
NSMutableString *result = [NSMutableString stringWithString:s];
CFStringFold((CFMutableStringRef)result, kCFCompareDiacriticInsensitive, NULL);
return result;
}
See the document of CFStringFold.
If you want unicode characters for localization/internationalization, you shouldn't embed the strings in the source code. Instead you should use Localizable.strings and NSLocalizedString. See here.
Note:
For arcane historical reasons, 'a' is an int in C, see the discussions here. In C++, it's a char. But it doesn't change the fact that writing more than one byte inside '...' is implementation-defined and not recommended. For example, see ISO C Standard 6.4.4.10. However, it was common in classic Mac OS to write the four-letter code enclosed in single quotes, like 'APPL'. But that's another story...
Another complication is that accented letters are not always represented by 1 byte; it depends on the encoding. In UTF-8, it's not. In ISO-8859-1, it is. And unichar should be in UTF-16. Did you save your source code in UTF-16? I think the default of XCode is UTF-8. GCC might do some encoding conversion depending on the setup, too...
Or you can just do it like this:
static unichar accent characters[] = { L'ā', L'á', L'ă', L'à' };
L is a standard C keyword which says "I'm about to write a UNICODE character or character set".
Works fine for Objective-C too.
Note: The compiler may give you a strange warning about too many characters put inside a unichar, but you can safely ignore that warning. Xcode just doesn't deal with the unicode characters the right way, but the compiler parses them properly and the result is OK.
Depending on your circumstances, this may be a tidy way to do it:
NSCharacterSet* accents =
[NSCharacterSet characterSetWithCharactersInString:#"āáăà"];
And then, if you want to check if a given unichar is one of those accent characters:
if ([accents characterIsMember:someOtherUnichar])
{
}
NSString also has many methods of its own for handling NSCharacterSet objects.

Raw strings like Python's in Objective-C

Does Objective-C have raw strings like Python's?
Clarification: a raw string doesn't interpret escape sequences like \n: both the slash and the "n" are separate characters in the string. From the linked Python tutorial:
>>> print 'C:\some\name' # here \n means newline!
C:\some
ame
>>> print r'C:\some\name' # note the r before the quote
C:\some\name
Objective-C is a superset of C. So, the answer is yes. You can write
char* string="hello world";
anywhere. You can then turn it into an NSString later by
NSString* nsstring=[NSString stringWithUTF8String:string];
From your link explaining what you mean by "raw string", the answer is: there is no built in method for what you are asking.
However, you can replace occurrences of one string with another string, so you can replace #"\n" with #"\\n", for example. That should get you close to what you're seeking.
You can use stringize macro.
#define MAKE_STRING(x) ##x
NSString *expendedString = MAKE_STRING(
hello world
"even quotes will be escaped"
);
The preprocess result is
NSString *expendedString = #"hello world \"even quotes will be escaped\"";
As you can see, double quotes are escaped, however new lines are ignored.
This feature is very suitable to paste some JS code in Objective-C files. Using this feature is safe if you are using C99.
source:
https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html
How, exactly, does the double-stringize trick work?
Like everyone said, raw ANSI strings are very easy. Just use simple C strings, or C++ std::string if you feel like compiling Objective C++.
However, the native string format of Cocoa is UCS-2 - fixed-width 2-byte characters. NSStrings are stored, internally, as UCS-2, i. e. as arrays of unsigned short. (Just like in Win32 and in Java, by the way.) The systemwide aliases for that datatype are unichar and UniChar. Here's where things become tricky.
GCC includes a wchar_t datatype, and lets you define a raw wide-char string constant like this:
wchar_t *ws = L"This a wide-char string.";
However, by default, this datatype is defined as 4-byte int and therefore is not the same as Cocoa's unichar! You can override that by specifying the following compiler option:
-fshort-wchar
but then you lose the wide-char C RTL functions (wcslen(), wcscpy(), etc.) - the RTL was compiled without that option and assumes 4-byte wchar_t. It's not particularly hard to reimplement these functions by hand. Your call.
Once you have a truly 2-byte wchar_t raw strings, you can trivially convert them to NSStrings and back:
wchar_t *ws = L"Hello";
NSString *s = [NSString stringWithCharacters:(const unichar*)ws length:5];
Unlike all other [stringWithXXX] methods, this one does not involve any codepage conversions.
Objective-C is a strict superset of C so you are free to use char * and char[] wherever you want (if that's what you call raw strings).
If you mean C-style strings, then yes.