NSString (or NSArray or something) to variable parameter list of C (char *) strings - objective-c

Is there any easy way to convert an Objective-C holding class of NSStrings into parameters for a function accepting a variable list of char *? Specifically I have a function like:
-(void)someFunction:(NSSomething *) var
that I want to forward to a C function like
void someCFunction(char * var, ...)
Is there an easy way to go about this?

No, you can only do what you want if the number of arguments you're passing is known at compile time. If you just want to convert a single string, use the -UTF8String message:
// Example with two strings
NSString *str1 = ...;
NSString *str2 = ...;
someCFunction([str1 UTF8String], [str2 UTF8String]); // etc.
But if the number of strings will vary at runtime, you'll need to use a different API, if one is available. For example, if there's an API that took an array of strings, you could convert the Objective-C array into a C array:
// This function takes a variable number of strings. Note: in C/Objective-C
// (but not in C++/Objective-C++), it's not legal to convert 'char **' to
// 'char *const *', so you may sometimes need a cast to call this function
void someCFunction(const char *const *stringArray, int numStrings)
{
...
}
...
// Convert Objective-C array to C array
NSArray *objCArray = ...;
int numStrings = [objCArray count];
char **cStrArray = malloc(numStrings * sizeof(char*));
for (int i = 0; i < count; i++)
cStrArray[i] = [[objCArray objectAtIndex:i] UTF8String];
// Call the function; see comment above for note on cast
someCFunction((const char *const *)cStrArray, numStrings);
// Don't leak memory
free(cStrArray);

This would do the trick:
NSString *string = #"testing string"
const char * p1=[string UTF8String];
char * p2;
p2 = const_cast<char *>(p1);

Yes, this can be done, and is explained here:
How to create a NSString from a format string like #"xxx=%#, yyy=%#" and a NSArray of objects?
And here:
http://www.cocoawithlove.com/2009/05/variable-argument-lists-in-cocoa.html
With modifications for ARC here:
How to create a NSString from a format string like #"xxx=%#, yyy=%#" and a NSArray of objects?
Also, variable arguments are not statically or strongly typed, as the other poster seems to be suggesting. In fact, there is no clear indication in the callee of how many arguments you really have. Determining the number of arguments generally breaks down into having to either specify the number by an count parameter, using a null terminator, or inferring it from a format string a la (s)print* . This is frankly why the C (s)print* family of functions has been the source of many errors, now made much much safer by the XCode / Clang / GCC compiler that now warns.
As an aside, you can approach statically typed variable arguments in C++ by creating a template method that accepts an array of an unspecified size. This is generally considered bad form though as the compiler generates separate instances for each size of array seen by by the compiler (template bloat).

Related

How to replace a char in an an char array? Xcode

i got the following char array in Objective-C (Xcode):
char *incomeMessage;
NSString *str = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding];
incomeMessage = [str UTF8String];
NSLog(#"%c", incomeMessage[0]);
NSLog(#"%c", incomeMessage[1]);
NSLog(#"%c", incomeMessage[2]);
NSLog(#"%c", incomeMessage[3]);
NSLog(#"%c", incomeMessage[4]);
NSLog(#"%c", incomeMessage[5]);
For example I get some results like this in console:
"3
2
6
1
8
4"
Now i want to replace the char in incomeMessage[2] by 4:
incomeMessage[2] = '4';
But then it gives me the error:
EXC_BAD_ACCESS
Do you have an idea, how to solve the problem?
According to the reference documentation, UTF8String returns a read-only (const char*) reference to the string data.
The reference material goes on to note:
This C string is a pointer to a structure inside the string object,
which may have a lifetime shorter than the string object and will
certainly not have a longer lifetime. Therefore, you should copy the C
string if it needs to be stored outside of the memory context in which
you use this property.
So I'd suggest following their advice and creating a copy of the array and then performing your modifications against that.
For example: http://ideone.com/mhjwZW
You might have better luck with something like:
NSString* str = [[NSString alloc] initWithBytes:data.bytes length:data.length encoding:NSUTF8StringEncoding];
char* incomeMessage = malloc([str lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1);
strcpy(incomeMessage, [str UTF8String]);
//now you can change things
incomeMessage[2] = '4';
//do this when you're done
free(incomeMessage);
Although, is there any particular reason why you want to use a C-string/character array as opposed to an NSMutableString? I think you might find replaceCharactersInRange:withString: a better approach generally. See also: stringByReplacingCharactersInRange:withString:.
i got the following char array in Objective-C (Xcode)
You don't, you know. All you have is a pointer. You have not set aside any actual memory; there is no array there.
incomeMessage = [str UTF8String];
All you've done in that line is repoint the pointer incomeMessage at your string's UTF8String. A string's UTF8String is immutable. Note this passage in the docs:
you should copy the C string if it needs to be stored outside of the memory context in which you use this property.
So basically, if you want to write into an array of char, your first task should be to make an array of char.

objective-c - Why can I assign values to pointers?

I understand pointers work with addresses and not the data itself. This is why I need to use the address-of (&) operator below as I need to assign the address of num to the pointer and not the actual value of num (40).
int num = 40;
int *numPtr = &num;
Therefore i'm confused as to why I can do this.
NSString *str = #"hello";
I've created a pointer str but instead of giving it an address i'm able to assign it some data, a literal string.
I thought pointers could only hold memory addresses so why am I able to directly assign it some data?
For someone trying to get their head around pointers and objects this is very confusing.
No you are not assigning a literal string to it, # makes a NSString object with the string value hello.
In most C languages strings are just an array of char, where char is a primitive type like int like in your example.
There is a reason you put an # before string literals (when you want an NSString and not a C string) in objective-c
#"String" is basically equivalent to [NSString stringWithCString:"string"] which returns a pointer to an NSString object containing the value "string"
It is the same way 1 is a c type integer, but #1 is a NSNumber representing the value of 1. If you see an # it means "this is shorthand for creating an object". (#[] for NSArrays, #{} for NSDictionarys, #(), #123, #YES, #NO for NSNumbers, and #"" for NSString)
C does not have strings. Usually char arrays are used to represent them.
NSString *str = #"hello";
can be thought of as short hand (literal) for:
char charArray[] = "hello";
NSString *str = [[NSString alloc] initWithBytes:charArray length:sizeof(charArray) encoding:NSUTF8StringEncoding]; // disregard character encoding for this example
or
unichar bla[] = {'h', 'e', 'l', 'l', 'o'};
str = [[NSString alloc] initWithCharacters:bla length:sizeof(bla)];
So an object is created and thus you need a pointer.

Having difficulties understanding pointers in Objective-C

So I understand that in ObjC everything lives in the Heap, and everything has a pointer to it. I'm reading through O'Reilys book and I'm grasping most things, but when I'm following through the tutorials/examples and something like this comes up
NSMutableArray *bar = [[[foo alloc] init] callMethod];
The * is right next to bar, but then you have things like
- (NSString *)createDeck:(NSString *)numOfCards;
Why is NSString * and not - (NSString)*createDeck:(NSString)*numOfCards;?
Any help understand things concept would be great thanks.
Edit:
NSUInteger *randomIndex = arc4random() % [deck count];
As Where
NSUInteger randomIndex = arc4random() % [deck count];
Works fine, how come removing the pointer in this case works?
tl;dr
The type is NSString * and that's why you have
- (NSString *)createDeck:(NSString *)numOfCards;
The return type and the argument type are enclosed within the parentheses.
Concerning the last question, an NSUInteger is not a object, despite the naming may suggest otherwise. Being a native type it lives on the stack and you don't need a pointer to it.
If you cmd-click on the type name, you'll find that it's actually a typedef for unsigned int (or unsigned long, depending on the architecture).
Discussion
Variables in C (and consequently in Objective-C) are declared using declarators, which are composed by a type and an identifier. In your example NSString * is the type and bar is the identifier.
NSString * bar;
^^^^^^^^^^ ^^^
type identifier
Declarators without an identifier are called abstract declarators, and are commonly used in C in three cases:
casting
float x = (float)2/4;
argument of sizeof()
sizeof(int *);
declaring argument types of a function
void foo(int *, float);
In Objective-C they are also used for return and argument types of methods, and that's why you have
- (NSString *)createDeck:(NSString *)numOfCards;
(Most of the information about declarators are adapted from http://nilsou.com/blog/2013/08/21/objective-c-blocks-syntax/)
Concerning the position of the asterkisk,
NSString *bar;
NSString * bar;
NSString* bar;
are all valid ways in to declare a variable of type pointer to NSString, aka NSString *.
Which one to use is a pure matter of personal taste, even though I believe the first one is the most common.

How do I convert a c-style char* array to NSArray?

a.H:
-(NSArray *) returnarray:(int) aa
{
unsigned char arry[1000]={"aa","vv","cc","cc","dd"......};
NSArray *tmpary=arry;
return tmpary;
}
a.c:
#include "a.H"
main (){
// how do I call returnarray function to get that array in main class
}
I need that array in main and I need to retain that array function in separate class.
Can someone please provide a code example to do this?
These lines:
unsigned char arry[1000]={"aa", "vv", "cc", "cc", "dd", ...};
NSArray *tmpary=arry;
Should instead be:
unsigned char arry[1000]={"aa", "vv", "cc", "cc", "dd", ...};
NSMutableArray * tmpary = [[NSMutableArray alloc] initWithCapacity: 1000];
for (i = 0; i < 1000; i++)
{
[tmpary addObject: [NSString stringWithCString: arry[i] encoding:NSASCIIStringEncoding]];
}
This is because a C-style array (that is, int arr[10]; for example) are not the same as actual NSArray objects, which are declared as above.
In fact, one has no idea what an NSArray actually is, other than what the methods available to you are, as defined in the documentation. This is in contrast to the C-style array, which you are guaranteed is just a contiguous chunk of memory just for you, big enough to hold the number of elements you requested.
C-style arrays are not NSArray's so your assignment of arry (the definition of which has some typos, at least the unsighned part) is not valid. In addition, you call arry an array of char, but you assign it an array of null-terminated strings.
In general you need to loop and add all the elements of the C-style array to the NSArray.
I'm not sure why you must do it in main. If you want a global you can do it by declaring a global in another file. That said, you CANNOT assign a plain C data array to an objective C NSArray, which is different in nature entirely.

Passing and calling dynamic blocks in Objective C

As part of a unit test framework, I'm writing a function genArray that will generate NSArrays populated by a passed in generator block. So [ObjCheck genArray: genInt] would generate an NSArray of random integers, [ObjCheck genArray: genChar] would generate an NSArray of random characters, etc. In particular, I'm getting compiler errors in my implementation of genArray and genString, a wrapper around [ObjCheck genArray: genChar].
I believe Objective C can manipulate blocks this dynamically, but I don't have the syntax right.
ObjCheck.m
+ (id) genArray: (id) gen {
NSArray* arr = [NSMutableArray array];
int len = [self genInt] % 100;
int i;
for (i = 0; i < len; i++) {
id value = gen();
arr = [arr arrayByAddingObject: value];
}
return arr;
}
+ (id) genString {
NSString* s = #"";
char (^g)() = ^() {
return [ObjCheck genChar];
};
NSArray* arr = [self genArray: g];
s = [arr componentsJoinedByString: #""];
return s;
}
When I try to compile, gcc complains that it can't do gen(), because gen is not a function. This makes sense, since gen is indeed not a function but an id which must be cast to a function.
But when I rewrite the signatures to use id^() instead of id, I also get compiler errors. Can Objective C handle arbitrarily typed blocks (genArray needs this), or is that too dynamic?
Given that blocks are objects, you can cast between block types and id whenever you want, though if you cast the block to the wrong block type and call it, you're going to get unexpected results (since there's no way to dynamically check at runtime what the "real" type of the block is*).
BTW, id^() isn't a type. You're thinking of id(^)(). This may be a source of compiler error for you. You should be able to update +genArray: to use
id value = ((id(^)())(gen))();
Naturally, that's pretty ugly.
*There actually is a way, llvm inserts an obj-c type-encoded string representing the type of the block into the block's internal structure, but this is an implementation detail and would rely on you casting the block to its internal implementation structure in order to extract.
Blocks are a C-level feature, not an ObjC one - you work with them analogously to function pointers. There's an article with a very concise overview of the syntax. (And most everything else.)
In your example, I'd make the gen parameter an id (^gen)(). (Or possibly make it return a void*, using id would imply to me that gen generates ObjC objects and not completely arbitrary types.)
No matter how you declare your variables and parameters, your code won't work. There's a problem that runs through all your compiler errors and it would be a problem even if you weren't doing convoluted things with blocks.
You are trying to add chars to an NSArray. You can't do that. You will have to wrap them them as some kind of Objective C object. Since your only requirement for this example to work is that the objects can be inputs to componentsJoinedByString, you can return single-character NSStrings from g. Then some variety of signature like id^() will work for genArray. I'm not sure how you parenthesize it. Something like this:
+ (id) genArray: (id^()) gen;
+ (id) genString {
...
NSString * (^g)() = ^() {
return [NSString stringWithFormat:#"%c", [ObjCheck genChar]];
};
...
}
NSString * is an id. char is not. You can pass NSString * ^() to id ^(), but you get a compiler error when you try to pass a char ^() to an id ^(). If you gave up some generality of genArray and declared it to accept char ^(), it would compile your call to genArray, but would have an error within genArray when you tried to call arrayByAddingObject and the argument isn't typed as an id.
Somebody who understands the intricacies of block syntax feel free to edit my post if I got some subtle syntax errors.
Btw, use an NSMutableArray as your local variable in genArray. Calling arrayByAddingObject over and over again will have O(n^2) time performance I imagine. You can still declare the return type as NSArray, which is a superclass of NSMutableArray, and the callers of genArray won't know the difference.