Convert dispatch_data_t to c++ std::string or CFStringRef

I have been working with Apple's Network Framework to implement a WebSocket Client. Here is the receive block for my code:

	nw_connection_receive(connection, 1, UINT32_MAX, ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) {

		nw_retain(context);
		
		nw_protocol_metadata_t wsMetadata = nw_content_context_copy_protocol_metadata(context,nw_protocol_copy_ws_definition());
		nw_ws_opcode_t inputOpCode = nw_ws_metadata_get_opcode(wsMetadata);
		fprintf(stderr,"\n\nInput Opcode: %d\n", inputOpCode);
		
		dispatch_block_t schedule_next_receive = ^{
			// If the context is marked as complete, and is the final context,
			// we're read-closed.
			if (is_complete &&
				(context == NULL || nw_content_context_get_is_final(context))) {
				exit(0);
			}

			// If there was no error in receiving, request more data
			if (receive_error == NULL) {
				receive_loop(connection);
			}
			nw_release(context);
		};

		if (content != NULL) {
			// If there is content, write it to stdout asynchronously
			schedule_next_receive = Block_copy(schedule_next_receive);
			
			dispatch_write(STDOUT_FILENO, content,dispatch_get_main_queue(), ^(__unused dispatch_data_t _Nullable data, int stdout_error) {
				if (stdout_error != 0) {
					errno = stdout_error;
					fprintf(stderr,"stdout write error\n");
				} else {
					schedule_next_receive();
				}
				Block_release(schedule_next_receive);
			});		
		} else {
			// Content was NULL, so directly schedule the next receive
			schedule_next_receive();
		}

	});

This code block is taken from (Implementing netcat with Network Framework | Apple Developer Documentation). I now want to get rid of the dispatch_write block and convert the received dispatch_data_t object to a string representation either a CFStringRef or a C++ std::string.

Accepted Answer

The easiest way to do this is to got via Objective-C:

static CFStringRef QCFStringCreateWithDispatchData(dispatch_data_t d) {
    return CFBridgingRetain( [[NSString alloc] initWithData:(NSData *) d encoding:NSUTF8StringEncoding] );
}

This exploits the fact that dispatch_data_t is toll-free bridged to NSData.

If you can’t use Objective-C then you can do this:

static CFStringRef QCFStringCreateWithDispatchData2(dispatch_data_t d) {
    const void * buf = NULL;
    size_t bufSize = 0;
    dispatch_data_t d2 = dispatch_data_create_map(d, &buf, &bufSize);
    CFRetain((__bridge CFTypeRef) d2);
    CFStringRef result = CFStringCreateWithBytes(NULL, buf, (CFIndex) bufSize, kCFStringEncodingUTF8, true);
    CFRelease((__bridge CFTypeRef) d2);
    return result;
}

Note that I manually retain d2 to guarantee that buf persists until after the call to CFStringCreateWithBytes.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Hello Quinn,

Thanks for the quick reply. Writing this again as a reply because the comment is completely illegible. Sorry about that, new to these forum pages.

I am writing my code in C++ and using the g++ compiler command g++ -std=c++14 -framework Security -framework CoreFoundation -framework Foundation -framework Network -o WebSocket WebSocket.cpp . I had to comment out the CFRetain((__bridge CFTypeRef) d2); and CFRelease((__bridge CFTypeRef) d2); lines as I was getting the following errors:

_error: unknown type name '__bridge'
    CFRelease((__bridge CFTypeRef) d2);
               ^
error: expected ')'
    CFRelease((__bridge CFTypeRef) d2);
                        ^
note: to match this '('
    CFRelease((__bridge CFTypeRef) d2);_

Is this okay? Is there a way to get the ARC stuff working for C++ compilers. My knowledge in this area is absolutely zero.

Also, had to change the call to CFStringCreateWithBytes with CFStringRef result = CFStringCreateWithBytes(NULL, reinterpret_cast<unsigned char*>(const_cast<void*>(buf)), (CFIndex) bufSize, kCFStringEncodingUTF8, true); because of error:

_candidate function not viable: cannot convert argument of incomplete type 'const void *' to 'const UInt8 *' (aka 'const unsigned char *') for 2nd argument CFStringRef CFStringCreateWithBytes(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean isExternalRepresentation);_

because the comment is completely illegible.

Yeah. You’re not the first person to get confused by the nature of comments and I’m still hoping to see improvements in this space (r. 80839588)

Is this okay?

No.

If you’re not using ARC then you are responsible for manually releasing d2 by calling dispatch_release. In that case you can call dispatch_release after calling CFStringCreateWithBytes and so you don’t need the CFRetain / CFRelease pair.

Is there a way to get the ARC stuff working for C++ compilers.

Clang supports ARC in both its Objective-C and Objective-C++ forms. The best way to work out the right options to use is to create a tiny Xcode project, build it, and then look at the build transcript to see what it’s doing. Start with the macOS > Command Line Tool template, choose Objective-C, and then rename main.m to main.mm to get Objective-C++.

Oh, and I’m assuming that your g++ ends up pointing at Clang. If you’ve installed GCC and g++ points there, then I don’t have any answers for you.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Convert dispatch_data_t to c&#43;&#43; std::string or CFStringRef
 
 
Q