How can I fix a cast of an indirect pointer (under ARC) in this code?-Collection of common programming errors

So we’re trying to walkthru a SQLite example which implements NSFastEnumeration, provided by Lynda.com. Converting it to ARC has been an issue for me as I have been unable to fix the error

Cast of an indirect pointer to an Objective-C pointer to 'va_list' (aka char *) is      disallowed with ARC.

It is pointing to this line and the va_list cast

[self bindSQL:[query UTF8String] arguments:(va_list)values];

which is in this function where values is defined (__unsafe_unretained was added to fix some other error)

- (NSNumber *) insertRow:(NSDictionary *) record 
{
    int dictSize = [record count];
    __unsafe_unretained id values[dictSize];

The implementation of bindSQL is

- (void) bindSQL:(const char *) cQuery arguments:(va_list)args

How is this corrected?

Thanks for the interest guys. As requested here is the original bindSQL function and the calling insertRow function

- (void) bindSQL:(const char *) cQuery arguments:(va_list)args {
// NSLog(@"%s: %s", __FUNCTION__, cQuery);
int param_count;

// preparing the query here allows SQLite to determine
// the number of required parameters
if (sqlite3_prepare_v2(database, cQuery, -1, &statement, NULL) != SQLITE_OK) {
    NSLog(@"bindSQL: could not prepare statement (%s)", sqlite3_errmsg(database));
    statement = NULL;
    return;
}

if ((param_count = sqlite3_bind_parameter_count(statement))) {
    for (int i = 0; i < param_count; i++) {
        id o = va_arg(args, id);

        // determine the type of the argument
        if (o == nil) {
            sqlite3_bind_null(statement, i + 1);
        } else if ([o respondsToSelector:@selector(objCType)]) {
            if (strchr("islISLB", *[o objCType])) { // integer
                sqlite3_bind_int(statement, i + 1, [o intValue]);
            } else if (strchr("fd", *[o objCType])) {   // double
                sqlite3_bind_double(statement, i + 1, [o doubleValue]);
            } else {    // unhandled types
                NSLog(@"bindSQL: Unhandled objCType: %s", [o objCType]);
                statement = NULL;
                return;
            }
        } else if ([o respondsToSelector:@selector(UTF8String)]) { // string
            sqlite3_bind_text(statement, i + 1, [o UTF8String], -1, SQLITE_TRANSIENT);
        } else {    // unhhandled type
            NSLog(@"bindSQL: Unhandled parameter type: %@", [o class]);
            statement = NULL;
            return;
        }
    }
}

va_end(args);
return;

}

- (NSNumber *) insertRow:(NSDictionary *) record {
// NSLog(@"%s", __FUNCTION__);
int dictSize = [record count];

// the values array is used as the argument list for bindSQL
id keys[dictSize];  // not used, just a side-effect of getObjects:andKeys
id values[dictSize];
[record getObjects:values andKeys:keys];    // convenient for the C array

// construct the query
NSMutableArray * placeHoldersArray = [NSMutableArray arrayWithCapacity:dictSize];
for (int i = 0; i < dictSize; i++)  // array of ? markers for placeholders in query
    [placeHoldersArray addObject: [NSString stringWithString:@"?"]];

NSString * query = [NSString stringWithFormat:@"insert into %@ (%@) values (%@)",
                    tableName,
                    [[record allKeys] componentsJoinedByString:@","],
                    [placeHoldersArray componentsJoinedByString:@","]];

[self bindSQL:[query UTF8String] arguments:(va_list)values];
sqlite3_step(statement);
if(sqlite3_finalize(statement) == SQLITE_OK) {
    return [self lastInsertId];
} else {
    NSLog(@"doQuery: sqlite3_finalize failed (%s)", sqlite3_errmsg(database));
    return [NSNumber numberWithInt:0];
}

}

  1. It seems that your bindSQL:arguments: accepts va_list while you are passing an array of id‘s, instead.

    Therefore, ARC does not know how to deal with cast from id* to va_list. If it worked before turning on ARC, it was just you being lucky that some internal representation at your target machine matched. But you cannot be sure of it even without ARC as it can start giving you, unexpectedly, some nasty crashes.

  2. Craig,

    Why are you using a va_list at all? As you pull each individual item out of the list, you could easily just leave the data in the NSDictionary and pull them out with an -objectAtIndex: applied to the -allValues array. Then you’ve just avoided this problem altogether.

    Andrew

    P.S. Don’t fight the framework or runtime. You’ll be frustrated and will lose.

    Craig,

    I’m not sure what is wrong with your code but I was interested enough to write an example that does compile. I hope this helps you figure your problem out. My example is a traditional application of a va_list in a logging example. (This code was extracted from my more complex logging macros. Hence it primarily tests the language and doesn’t do anything novel.)

    First, the class:

    @interface Macros
    
    + (void) testWithVAList: (va_list) vaList;
    + (void) callTestWithVAList: (va_list) vaList;
    
    @end
    
    @implementation Macros
    
    + (void) testWithVAList: (va_list) vaList {
    } // -testWithVAList:
    
    + (void) callTestWithVAList: (va_list) vaList {
    
        [self testWithVAList: vaList];
    
    } // -callTestWithVAList:
    
    @end
    

    Now a standard C-function that calls the class:

    void testLog(NSString *format, ...);
    void testLog(NSString *format, ...) {
    
        va_list argp;
        va_start(argp, format);
    
        [Macros testWithVAList: argp];
    
        NSLogv(format, argp);
    
        va_end(argp);
    
    } // testLog()
    

    This code compiles just fine in Xcode v4.2.1 using clang v3.

    Notice that there are no __bridge or any other ARC type qualifiers for this native C-type. Hence, I don’t know what is wrong with your code. (Frankly, you didn’t give us enough code to really help you solve your problem.)

    In conclusion, a va_list is compatible with ARC. It is an independent of ARC type.

    Andrew