objective-c++ support

I am dealing with some legacy programs that use Objective-C++. The header files for their functions have extern "C", so I'm not sure how necessary it is to use C++ instead of C. There are references to writing applications in Objective-C++ in the past, but it appears that much of the Apple documentation on this has been removed. My question is whether I should be Objective-C for all files with functions that also call the various frameworks (Cocoa, Foundation, CoreFoundation, CoreMidi, CoreAudio, etc.


I see that CoreMIDI/MIDIServices.h has


#ifdef ++cplusplus

extern "C" {

#endif


at the top of the header file and


#ifdef __cplusplus

}

#endif


at the end, enabling the use in C++ by making C references. However, other header files, such as CoreFoundation/CFArray.h and CoreFoundation/CFDictionary.h do not, resulting in a statement that it is unable to find the functions since it is looking for a mashed version of the name. I suppose that I could create modified versions of the header files that would specify C function callls, but that seems undesirable to me. Since I am talking to others, I was hoping for some guidance on what is considered proper and what risks are run if I stick with Objective-C++.


In addition, what is the likelihood of the code for extern "C" being removed from the CoreMIDI header files in the future.

Replies

ObjectiveC++ is more of a mode than a language. For source files whose extension is ".mm", the clang compiler will permit both Obj-C and C++ syntax constructs in the same source file (which includes any headers included in the compliation, regardless of extension).


There is no source code ambiguity in class-related declarations, and therefore there should be no ambiguity in uses of declared types and variables throughout the code. However, there are some C language things that exist differently in Obj-C and C++, even when the syntax is the same or similar. The main case is, of course, functions declared at file scope, but there's a longer list including enums and structs.


The thing is, C++ already allows for C declarations, via the extern "C" { } construct. Anything that would be ambiguous will be interpreted as C++ unless enclosed in that construct. Obj-C really makes no difference in that regard — it's C++ vs. C, not C++ vs. Obj-C.


The presence of extern "C" { } inside MIDIServices.h probably indicates only that the file is included internally in some portion of the CoreMIDI source code that happens to be written in C++. In the case of (say) CoreFoundation, if you look more closely, you will see that header files like CFArray.h do contain extern "C" { }, but via CF_EXTERN_C_BEGIN and CF_EXTERN_C_END macros.


So, as to the question of riskiness, Objective-C++ remains a fully-supported mode (or language variant) in the clang compiler, and there's no reason to fear its disappearance. As to the question of usability, it ought to be straightforward to mix C++ and Obj-C freely, except that you must remember to disambiguate your own C language constructs with the presence or absence of extern "C" { }.


Does that answer your questions, or have I misunderstood your point?

I am unaware of anything that Apple does that would require Objective-C++. It is a convenience language for interacting with C++ code. If you don't use any C++, then you don't need it. Your biggest risk is being incompatible with Swift that all the cool kid are using now.

That is extremely useful. I hadn't realized what those two macros were.


The problem is that I am getting the message "No matching function for call to ..." for

CFArrayGetCount

CFArrayGetValueAtIndex

CFNumberGetValue

CFDictionaryGetCount

CFDictionaryGetKeysAndValues


The statements at the start of the source code are


#import <Foundation/Foundation.h>

#import <CoreFoundation/CoreFoundation.h>

#import <CoreMIDI/CoreMIDI.h>


I tried #include but that didn't work eiher nor did adding #define __cplusplus 1


The code works when it is an Objective-C file (.m)


Do you have any idea what I'm doing wrong?

The "No matching function" message sounds like a missing prototype, perhaps. I was able to create a single source file (in a vanilla macOS project created from a template) with the following contents:


#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <CoreMIDI/CoreMIDI.h>

void abcdef (void) {
     CFArrayRef arrayRef = CFArrayCreate (nil, NULL, 0, &kCFTypeArrayCallBacks);
     NSUInteger arrayLength = CFArrayGetCount (arrayRef);
}

class Abc {
     void abc (void) {
          CFArrayRef arrayRef = CFArrayCreate (nil, NULL, 0, &kCFTypeArrayCallBacks);
          NSUInteger arrayLength = CFArrayGetCount (arrayRef);
     }
};

@interface MyAbc: NSObject
- (void) myAbc;
@end

@implementation MyAbc
- (void) myAbc {
     CFArrayRef arrayRef = CFArrayCreate (nil, NULL, 0, &kCFTypeArrayCallBacks);
     NSUInteger arrayLength = CFArrayGetCount (arrayRef);
}
@end


This compiles without errors (apart from warnings about unused variables). This has calls to CoreFoundation from C, C++ and Obj-C. Can you post a small file that won't compile?

I tried going through some tasks to make sure of what I was doing


The following Objective-c ran okay. This was main.m



#import <CoreFoundation/CoreFoundation.h>

int main(int argc, const char ** argv) {

@autoreleasepool {

CFStringEncoding encoding = kCFStringEncodingMacRomanLatin1;

char *string1;

char *string2;

char start1[] = "First CString";

CFStringRef CF1 = CFStringCreateWithCString(NULL, (char *) &start1, encoding);

CFStringRef CF2 = CFSTR("Second CString");

string1 = (char *) CFStringGetCStringPtr(CF1, kCFStringEncodingMacRomanLatin1);

string2 = (char *) CFStringGetCStringPtr(CF2, kCFStringEncodingMacRomanLatin1);

CFStringRef CF3 = CFStringCreateWithCString(NULL, "Third CString", encoding);

printf ("%s::%s\r\n", string1, string2);

printf ("%s\r\n", CFStringGetCStringPtr(CF1, encoding));

printf ("%s\r\n", CFStringGetCStringPtr(CF2, encoding));

printf ("%s\r\n", CFStringGetCStringPtr(CF3, encoding));

CFStringRef list[100];

list[0] = CFSTR("First item");

list[1] = CFSTR("Second item");

list[2] = CFSTR("Third item");

list[3] = CFSTR("Fourth item");

list[4] = CFSTR("Fifth item");

CFArrayRef array = CFArrayCreate(NULL, (void **) &list, 4, &kCFTypeArrayCallBacks);

printf("%ld items\r\n", CFArrayGetCount(array));

}

}


When I tried using it as a Objective-C++ (main.mm), it gave me the message "No matching function for call 'CFArrayCreate' "


#import <CoreFoundation/CoreFoundation.h>

using namespace std;

int main(int argc, const char ** argv) {

@autoreleasepool {

CFStringEncoding encoding = kCFStringEncodingMacRomanLatin1;

char *string1;

char *string2;

/

char start1[] = "First CString";

CFStringRef CF1 = CFStringCreateWithCString(NULL, (char *) &start1, encoding);

CFStringRef CF2 = CFSTR("Second CString");

string1 = (char *) CFStringGetCStringPtr(CF1, kCFStringEncodingMacRomanLatin1);

string2 = (char *) CFStringGetCStringPtr(CF2, kCFStringEncodingMacRomanLatin1);

CFStringRef CF3 = CFStringCreateWithCString(NULL, "Third CString", encoding);

printf ("%s::%s\r\n", string1, string2);

printf ("%s\r\n", CFStringGetCStringPtr(CF1, encoding));

printf ("%s\r\n", CFStringGetCStringPtr(CF2, encoding));

printf ("%s\r\n", CFStringGetCStringPtr(CF3, encoding));

CFStringRef list[100];

list[0] = CFSTR("First item");

list[1] = CFSTR("Second item");

list[2] = CFSTR("Third item");

list[3] = CFSTR("Fourth item");

list[4] = CFSTR("Fifth item");

CFArrayRef array = CFArrayCreate(NULL, (void **) &list, 4, &kCFTypeArrayCallBacks);

}

}


I know that I could do this as Objective-C instead of having to use Objective-C++. However, I'm already being considered a busybody for suggesting unnecessary changes.


Thank you for your assistance. I'm sure I will scream about the obviousnes of the problem when I learn the answer, but I'm getting confused. When I removed the line for CFArrayCreate, the rest of the code worked fine. So CFStringCreareWithCString, CFStrinGetCStringPtr, and CFSTR work fine. However, I'm having problems with some of the others.

Whenever you're asking about a compiler or linker error, always show the full error text, verbatim.


I'm fairly certain the compiler has told you about the candidate for matching your call that it knows about and why it rejected it. Which shows that it knows about CFArrayCreate() just fine. The issue is that C++ has much stricter type matching rules than C. In particular, the values parameter has type const void** and you're attempting to pass void** (without the const). That difference is sufficient for it to say there's no match.

Which is to say, this "main" is a C++ function, not a C function. When I tried it (in a .mm file) I got the following annotation on the error message:


[…]/CFArray.h:174:12: Candidate function not viable: no known conversion from 'void **' to 'const void **' for 2nd argument


When I try the exact same function in a .m file, there's only a warning:


[…]/ViewController.m:80:42: Passing 'void **' to parameter of type 'const void **' discards qualifiers in nested pointer types

Thank you very much for your help. I misinterpreted some of the messages. I knew that It would be obvious once it was pointed out to me. I changed the cast to (const void **) and everything worked.


I know that this sounds crazy, but I hadn't realized that clicking on the message revealed the full message. I had therefore missed part of the message. It also hadn't occurred to me that C++ would treat calls to C type functions would be different than the treatment in C. For some reason, my error message in C++ was a little different than yours. I have no reason why. However, I did give you the verbatim message.

>> I hadn't realized that clicking on the message revealed the full message


In fact, even clicking on it currently doesn't reveal all of the information available. In some cases, there are annotations which show only in the Issues navigator pane (Command-5). These typically exist when the compiler tells you about things that conflict, even sometimes when they nearly conflict. Checking the Issues pane is just a habit to get into.


>> my error message in C++ was a little different than yours


It depends on the version of the compiler and some of the build settings.


The point here, I think, is that there's sort of nothing special going on. ".mm" are really just C++ source that can have embedded Obj-C declarations, and all of the normal rules apply.