calling a function with a char and a variable number of arguments - ios

so what i am trying to do is whenever i touch a button, it calls a function with both a char and some variable number of arguments (in this case two ints that are both worth 24).
- (IBAction)buttonPressed:(id)sender {
number = 24;
printf_to_debug("some text %d %d ",number, number);
//printf(" %d %d ",number, number);
}
however, when i call the function it seems to always change my ints to some big number, which i don't understand.
void printf_to_debug (char* fmt, ...) {
va_list args;
va_start( args, fmt );
printf(fmt, args);
charString = [NSString stringWithUTF8String: fmt];
charString = [NSString stringWithFormat:charString, args];
debugTextString = [NSString stringWithFormat:#"%#\r%#",charString, debugTextString];
va_end( args );
}
does anyone see a problem with my code?

You have to use vprintf() to print the arguments given by a va_list:
va_list args;
va_start (args, fmt );
vprintf(fmt, args);
va_end( args );
The corresponding NSString method is initWithFormat:arguments::
NSString *s = [[NSString alloc] initWithFormat:#(fmt) arguments:args];

Let's take a look at what your code actually does.
The first important line is: printf_to_debug("some text %d %d ",number, number);
Which looks fine, it doesn't really matter what number is it'll be treated as an int anyways.
Next you initialize the variable arguments list which is fine but then you try and print using:
printf(fmt, args);
Now, When you use that code, you seem to want printf to treat each individual object in your variable arguments list as a separate parameter, however, printf doesn't care. All it sees is that you passed it a parameter. It doesn't know or care what the parameter was, all it knows and cares about is that you told it to treat the first parameter it was given as a decimal number (that's what the "%d" says). So now, it takes va_list and treats it as a number and prints it. Since you only provided one argument, the second number you get is complete garbage, and I suspect XCode tried to warn you about that (command line clang does).
Now for the simple solution:
When your trying to print a variable arguments list as seperate objects use the vprintf function. So in your case it would be vprintf(fmt, args);

Related

Passing an object that undergoes default argument promotion to 'va_start'

This is my first Xcode app and objective-c so give me some slack :)
I tried googling on the issue but I cannot see any help regarding Xcode and app development. I added the error masseages after //
- (id)initWithBytes:(int8_t)byte1, ... { //Error: 1. Parameter of type 'int8_t' (aka 'signed char') is declared here
va_list args;
va_start(args, byte1); //Error: Passing an object that undergoes default argument promotion to 'va_start' has undefined behavior
unsigned int length = 0;
for (int8_t byte = byte1; byte != -1; byte = va_arg(args, int)) {
length++;
}
va_end(args);
if ((self = [self initWithLength:length]) && (length > 0)) {
va_list args;
va_start(args, byte1); // Error: Passing an object that undergoes default argument promotion to 'va_start' has undefined behavior
int i = 0;
for (int8_t byte = byte1; byte != -1; byte = va_arg(args, int)) {
_array[i++] = byte;
}
va_end(args);
}
return self;
}
Thank you in advance!!
va_start() saves the pointer to the first argument passed to the function into a va_list.
The arguments themselves are passed via a hardware stack.
The issue with int8_t comes from the way the hardware stack is implemented. (in x86 at least)
Just like the SSE and MMX does, the stack requires elements stored on it to have an alignment equal to a multiple of 16bits, so everything passed to the function WILL have at least 16 bits of size, regardless of its type.
But the problem is va_arg() doesn't know about all that. Historically, it was a macro, and all it does is returning a pointer stored in va_list, and incrementing va_list by sizeof(type).
So, when you retrieve the next argument, the pointer returned does not point to the next argument but one byte before it, or not - depending on whether the va_arg is a macro or a compiler built-in function.
And this is what a warning is about.
IMO at least. Pardon my English, It's my 2nd language.

iOS 64Bit converting dont work in a method with va_list

I am trying to make an iOS-app ready for 64bit.
I got a method which build me a string with entries from an enum. The parameters from this method can be variable in count.
The method works fine under 32bit, but under 64bit my for-loop cant end correctly.
Here some code from .h:
#define enumToString(intVal) \
[NSString stringWithFormat: #"%ld", intVal]
#define ENUM_END -1
typedef enum _MYENTRIES
{
entry1,
entry2,
entry3
} MYENTRIES;
typedef NSUInteger MYENTRY;
My crashing method: (the loop has to end, but it doesn't end)
-(NSString*) getMyString:(MYENTRY) firstArg, ... {
va_list args;
va_start(args, firstArg);
NSMutableString *mySTRING = [[[NSMutableString alloc] init] autorelease];
for (MYENTRY arg = firstArg; arg != ENUM_END; arg = va_arg(args, MYENTRY))
{
NSLog(#"arg: %d %#", arg, enumToString(arg)); // when ENUM_END: "arg: -1 4294967295"
[mySTRING appendString:self.myDictionary[[NSString stringWithFormat: #"%ld", arg]]];
}
An example methodcall:
myString = [myClass getMyString: entry1, entry3, ENUM_END , nil];
Hope you can help me.
best regards
That's because MYENTRIES and MYENTRY are different types. One is 32 bit, and the other is 64 bit. Passing 32 bit values and reading them as 64 bit isn't going to work.
That's what the NS_ENUM macro is there for. Google for it, understand it, and use it. You will also have the problem that ENUM_END is not the same type as either MYENTRIES or MYENTRY (it is an int). Unless you can quote the rules for type conversion between int and unsigned long by heart (which you can't), I suggest you make it part of the enum.

Trouble Comparing Bluetooth-Sent ASCII char in iOS

I have an iOS application that talks to a RedBearLab Arduino device. My code that I use to send an int via bluetooth from Arduino to iOS is as follows:
void sendMyInt(int myInt) {
char b[4];
String str;
str=String(myInt);
str.toCharArray(b,4);
for (int i; i < 3; i++) {
char toPrint = b[i];
ble_write(toPrint);
}
}
Here is my code on the receiving end:
-(void) bleDidReceiveData:(unsigned char *)data length:(int)length
{
NSData *d = [NSData dataWithBytes:data length:length];
NSLog([NSString stringWithFormat:#"%#",d]);
NSString *s = [[NSString alloc] initWithData:d encoding:NSUTF8StringEncoding];
const char* clearChar = "!";
if ([self.label.text isEqualToString:#"Label"]) {
self.label.text = #"";
}
else if ([s isEqualToString:[NSString stringWithUTF8String:clearChar]]) {
self.label.text = #"";
}
else {
NSString *store = self.label.text;
NSString *full = [NSString stringWithFormat:#"%#%#",store,s];
self.label.text = full;
}
}
The final else statement fires somewhat as expected, and a value of 233! is printed out to the label over and over again, getting longer each time eventually forming things like 233!233!233! etc. As you can see, I am using a character (!) sent over a function to clear the label, but it never clears. The integer is the ASCII code for the exclamation point:
void clearLabel() {
int clearString = 33;
char excalamtion = clearString;
ble_write(excalamtion);
}
Why would this not clear the label? I assume it has something to do with the clashing formats, but I'm not really too good at that even after reading some documentation. For the else if statement I also tried this
if ([s isEqualToString:#"!"])
but that didn't work out either... Any help would be appreciated.
EDIT:
I forgot to put in my loop code so you can see function calls. Here it is:
void loop()
{
if ( ble_connected() ) {
int a = 223;
sendMyInt(a);
delay(1000);
clearLabel();
delay(1000);
}
ble_do_events();
}
EDIT 2:
Based on a suggestion by #Duncan C , I have isolated the problem to the fact that the data is being sent as one packet to the iPhone. Upon printing out my generated string when the data is received, the string 233! is received all at once rather than individual chars of 2 3 3, and one second later the signal to clear, !. The data takes two seconds to appear on my phone, indicating that both delays are being used. I need a way to separate the 2 3 3 packet from the ! packet.
First off, this line:
NSLog([NSString stringWithFormat:#"%#",d]);
Is sort of pointless. The stringWithFormat serves no real purpose, since NSLog takes a format string anyway.
Use this instead:
NSLog(#"%#", d);
You should probably also log the contents of "s" once you convert your NSData to an NSString. That will help you figure out what's going on.
What is likely going on is that your string is coming in as "233!", all together, 4 bytes at a time (assuming that your integer is == 233).
Your string is unlikely to ever contain just "!". Instead, it will likely contain "233!" (4 characters.) I say likely because it depends on how the data is packetized into BLE. Something that short should be sent all in 1 BLE packet, so you should get the entire string together.
You could use the NSString method rangeOfString: to search for your "!" string, and if it contains an "!", clear your label, but that won't really do any good either. If you're sending "233!", then the iOS code will see the exclamation point in the string it receives and simply clear the label.
Or does your arduino project first send "233", then after some other event, send the "!". You didn't make that clear.
Another problem: What does the Arduino String class do if the integer is less than 1000, or less than 100, and doesn't require 3 or 4 characters to convert to a char array? What is stored in the unused bytes? You're always sending 4 characters, which is probably wrong.
Adding in another ble_do_events(); after calling the sendMyInt(); function causes the data to be transmit in two separate packets.

NSLog() vs printf() when printing C string (UTF-8)

I have noticed that if I try to print the byte array containing the representation of a string in UTF-8, using the format specifier "%s", printf() gets it right but NSLog() gets it garbled (i.e., each byte printed as-is, so for example "¥" gets printed as the 2 characters: "¬•").
This is curious, because I always thought that NSLog() is just printf(), plus:
The first parameter (the 'format') is an Objective-C string, not a C
string (hence the "#").
The timestamp and app name prepended.
The newline automatically added at the end.
The ability to print Objective-C objects (using the format "%#").
My code:
NSString* string;
// (...fill string with unicode string...)
const char* stringBytes = [string cStringUsingEncoding:NSUTF8Encoding];
NSUInteger stringByteLength = [string lengthOfBytesUsingEncoding:NSUTF8Encoding];
stringByteLength += 1; // add room for '\0' terminator
char* buffer = calloc(sizeof(char), stringByteLength);
memcpy(buffer, stringBytes, stringByteLength);
NSLog(#"Buffer after copy: %s", buffer);
// (renders ascii, no matter what)
printf("Buffer after copy: %s\n", buffer);
// (renders correctly, e.g. japanese text)
Somehow, it looks as if printf() is "smarter" than NSLog(). Does anyone know the underlying cause, and if this feature is documented anywhere? (Couldn't find)
NSLog() and stringWithFormat: seem to expect the string for %s
in the "system encoding" (for example "Mac Roman" on my computer):
NSString *string = #"¥";
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding());
const char* stringBytes = [string cStringUsingEncoding:enc];
NSString *log = [NSString stringWithFormat:#"%s", stringBytes];
NSLog(#"%#", log);
// Output: ¥
Of course this will fail if some characters are not representable in the system encoding. I could not find an official documentation for this behavior, but one can see that using %s in stringWithFormat: or NSLog() does not reliably work with arbitrary UTF-8 strings.
If you want to check the contents of a char buffer containing an UTF-8 string, then
this would work with arbitrary characters (using the boxed expression syntax to create an NSString from a UTF-8 string):
NSLog(#"%#", #(utf8Buffer));

EXC_BAD_ACCESS error when using NSString getCString

I'm trying to parse some HTML. I use stringWithContentsOfURL to get the HTML. I attempt to load this into a character array so I can parse it, but I crash with the EXC_BAD_ACCESS error when getCString is called. Here is the relavent code:
- (void)parseStoryWithURL:(NSURL *)storyURL
{
_paragraphs = [[NSMutableArray alloc] initWithCapacity:10];
_read = NO;
NSError* error = nil;
NSString* originalFeed = [NSString stringWithContentsOfURL:storyURL encoding:NSUTF8StringEncoding error:&error];
_i = originalFeed.length;
char* entireFeed = malloc(_i*sizeof(char));
char* current = entireFeed;
char* lagger;
char* recentChars = malloc(7);
BOOL collectRecent = NO;
BOOL paragraphStarted = NO;
BOOL paragraphEnded = NO;
int recentIndex = 0;
int paragraphSize = 0;
NSLog(#"original Feed: %#", originalFeed);
_read = [originalFeed getCString:*entireFeed maxLength:_i encoding:NSUTF8StringEncoding];
I've also tried this passing the 'current' pointer to getCString but it behaves the same. From what I've read this error is typically thrown when you try to read from deallocated memory. I'm programming for iOS 5 with memory management. The line before that I print the HTML to the log and everything is fine. Help would be appreciated. I need to get past this error so I can test/debug my HTML parsing algorithms.
PS: If someone with enough reputation is allowed to, please add "getCString" as a tag. Apparently no one uses this function :(
There are several issues with your code - you're passing the wrong pointers and not reserving enough space. Probably the easiest is to use UTF8String instead:
char *entireFeed = strdup([originalFeed UTF8String]);
At the end you'll have to free the string with free(entireFeed) though. If you don't modify it you can use
const char *entireFeed = [originalFeed UTF8String];
directly.
If you want to use getCString, you'll need to determine the length first - which has to include the termination character as well as extra space for encoded characters, so something like:
NSUInteger len = [originalFeed lengthOfBytesUsingEncoding: NSUTF8StringEncoding] + 1;
char entireFeed[len];
[originalFeed getCString:entireFeed maxLength:len encoding:NSUTF8StringEncoding];
Try explicitly malloc'ing entireFeed with a length of _i (not 100% certain of this, as NSUTF8String might also include double byte unichars or wchars) instead of the wacky char * entireFeed[_i] thing you're doing.
I can't imagine char * entireFeed[_i] is working at run-time (and instead, you're passing a NULL pointer to your getCString method).
A few strange things;
char* entireFeed[_i]; allocates an array of char*, not an array of char. I suspect you want char entireFeed[_i] or char *entireFeed = malloc(_i*sizeof(char));
getCString takes a char* as a first parameter, that is, you should send it entireFeed instead of *entireFeed.
Also, note that the (UTF-8) encoding may add bytes to the result, so allocating the buffer the exact size of the input may cause the method to return NO (buffer too small). You should really use [originalFeed UTF8String] instead.

Resources