I have some Objective-C++ that wraps an existing C++ library. The C++ library occasionally gives me std::string&
parameters that I want to convert into nonnull
NSString
s. Almost all of the NSString
initializers return nullable NSString
s except: -initWithCharactersNoCopy:length:freeWhenDone:
and -initWithCharacters:length:
. However, both of those require unichar *
, but std::string.c_str()
returns char *
.
How can I get a unichar *
from a C++ std::string
so that I can create an NSString * _Nonnull
?
Not a duplicate
Other questions simply want to convert from std::string
to NSString
. I want to know if it's possible to do so without producing a null
NSString *
, possibly by calling methods on std::string
to get unichar *
. std::wstring_convert
looks promising, but I'm not a C++ developer, so I don't know how to get started with that yet.
Possible solution
After researching further, I learned that std::string
is just a std::basic_string<char>
, and it seems like you can define your own std::basic_string
s. I found a similar example that converts to std::wstring
:
// std::string -> std::wstring
std::string s("string");
std::wstring ws;
ws.assign(s.begin(), s.end());
So I adapted it to std::basic_string<unichar>
, and it compiles fine:
void Foo(const std::string& bar) {
std::basic_string<unichar> barUnichar;
barUnichar.assign(bar.begin(),
bar.end());
NSString * _Nonnull barNSString =
[NSString stringWithCharacters:barUnichar.c_str()
length:barUnichar.length()];
NSLog(@"bar: %@", bar);
}
I don't mind that converting from std::string
to std::basic_string<unichar>
performs a needless copy, and I imagine I can change the above code to use -[NSString initWithCharactersNoCopy:length:freeWhenDone:]
once I learn more about C++ memory ownership rules.
Possible solution is no good
That's going to do a byte-by-byte mapping. It may work for ASCII but will give you garbage for any Unicode.
Let's try again, see if it helps you. You have edited the question in response to the suggestion it is a duplicate and added:
Other questions simply want to convert from
std::string
toNSString
. I want to know if it's possible to do so without producing anull
NSString *
...
The direct answer: No
The reason is straightforward: The Objective-C library cannot assume that any pointer passed to it references a validly encoded C string, even if that pointer is typed std::string
.
Now you may be very confident that your C++ code will never pass you an invalidly encoded string – and it can be argued that confidence is reasonable (but then it is C++ after all ;-)) – and so believe the null result will never happen, but that does not change the fact that the Objective-C library cannot assume it won't.
The DIY answer: Yes
Trying to avoid the null, e.g. by trying to convert the std::string
into a unichar *
etc. as you have considered, just avoids the issue - some piece of code somewhere has to deal with checking the encoding or risk returning an invalidly encoded NSString
.
While it might be possible to do this it will both be more involved, and may leave you not knowing what is returned for invalid encodings, compared to the DIY approach: just deal with the null return yourself at source and replace it by something else. For example:
std::string someCstring;
NSString *convertedString = @(someCstring.c_str()) ?: @"ERROR: C string is invalid UTF8";
Here convertedString
will never be nil
.
(If your C string is not UTF8 you will need to use another NSString
initialiser which takes an encoding.)
HTH