5

I have a library which uses wstring extensively. I need to output changes and external data using NSLog Is there a simple way (not too expensive) to output the wstring using an intermediate function. Converting each wstring to NSString using va_list is the only way I can think of right now.


Edit: More precisions. I have a multi platform lib. I added a logging macro MYLog.


Edit I have to call my MYLog from C++ and I don't have access to Objective-C at that point. So the problem is that I can't convert the std::wstring before I do the call to MYLog.


Through MYLog I would like to be able to use NSLog or an intermediate like follows:

MYLog("Received %ls(%d) from user %ls %ls cp: %ls /nRAW:/t%ls",
       &d.name, d.id, &d.user.firstName, &d.user.lastName,
       &d.caption, &d.rawText);

Here (originally from here) I found this nice addition to NSString:

@interface NSString (cppstring_additions)
+(NSString*) stringWithwstring:(const std::wstring&)string;
+(NSString*) stringWithstring:(const std::string&)string;
-(std::wstring) getwstring;
-(std::string) getstring;
@end


@implementation NSString (cppstring_additions)

#if TARGET_RT_BIG_ENDIAN
const NSStringEncoding kEncoding_wchar_t = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32BE);
#else
const NSStringEncoding kEncoding_wchar_t = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32LE);
#endif

+(NSString*) stringWithwstring:(const std::wstring&)ws
{
    char* data = (char*)ws.data();
    unsigned size = ws.size() * sizeof(wchar_t);

    NSString* result = [[NSString alloc] initWithBytes:data length:size encoding:kEncoding_wchar_t];
    return result;
}

+(NSString*) stringWithstring:(const std::string&)s
{
    NSString* result = [[NSString alloc] initWithUTF8String:s.c_str()];
    return result;
}

-(std::wstring) getwstring
{
    NSData* asData = [self dataUsingEncoding:kEncoding_wchar_t];
    return std::wstring((wchar_t*)[asData bytes], [asData length] / sizeof(wchar_t));
}

-(std::string) getstring
{
    return [self UTF8String];
}

@end

All I could think of without wasting too much time (of mine) was to create an intermediate function called by my MYLog:

#define LAD(data) [logString appendFormat:[NSString stringWithFormat:@"%%%@", c], data]
#define LAP(type) LAD(va_arg(listPointer, type))

void MyLogImplementation(NSString* message, ...)
{
NSMutableString* logString = [[NSMutableString alloc] init];

    va_list listPointer;
    va_start(listPointer, message);

NSArray* lmc = [message componentsSeparatedByString:@"%"];

int counter = 0;
//NSLog(@"there are %d components in %@.", [lmc count], message);
BOOL ignoreNext = NO;
for (NSString* c in lmc)
{
    //NSLog(@"Testing %@", c);

    if (ignoreNext)
    {
        [logString appendFormat:@"%%%@", c];
        ignoreNext = NO;
    }
    if (0 == [c length])
    {
        ignoreNext = !ignoreNext;
    }
    else if (0 == counter && '%' != [message characterAtIndex:0] )
    {
        [logString appendFormat:@"%@", c];
    }
    else 
    {
        switch ([c characterAtIndex:0])
        {
            case 'd':
            case 'i':
                LAP(int);
                break;

            case 'X':
            case 'x':
                LAP(int);
                break;

            case '@':
                LAP(NSObject*);
                break;

            case 'f':
                LAP(double);
                break;

            case 'c':
                LAP(char);
                break;

            case 'l':
                switch ([c characterAtIndex:1])
            {
                case 's':
                {
                    std::wstring* str = va_arg(listPointer, std::wstring*);
                    NSString* nstr = [NSString stringWithwstring:str];

                    [logString appendFormat:@"%@", nstr];

                    [nstr release];
                    if (2 < [c length])
                    {
                        [logString appendString:[c substringFromIndex:2]];
                    }
                }
                    break;
            }
                break;
            default:
                [logString appendFormat:[NSString stringWithFormat:@"%%%@", c]];

        }
    }
    ++counter;
}


    NSLog(@"%@", logString);
[logString release];
    va_end(listPointer);
}

But this - it seems to me - is very inefficient. Is there a better way to achieve what i'm doing? I know I could simply transform this to:

NSLog(@"Received %@(%d) from user %@ %@ cp: %@ /nRAW:/t%@",
       [NSString stringWithwstring:d.name], d.id,
       [NSString stringWithwstring:d.user.firstName],
       [NSString stringWithwstring:d.user.lastName],
       [NSString stringWithwstring:d.caption],
       [NSString stringWithwstring:d.rawText]);

And the problem would be solved. But I would loose the multiplaform advantage... I guess.

Community
  • 1
  • 1
Coyote
  • 2,454
  • 26
  • 47
  • Can you fill in the details of the NSLog calls – Cheers and hth. - Alf Nov 26 '11 at 12:40
  • @AlfP.Steinbach yes... I added more details on the subject. I know I'm overlooking the most obvious solution, hiding behind multi platform rhetorics, but I would really appreciate to be able to keep as much of my app code as possible free from platform dependencies and hide as much of it behind my layer of implementations. – Coyote Nov 27 '11 at 09:32

2 Answers2

5

You could convert to an NSString and log that. This is a NSString category taken from here. Don't know how well it works but it looks sensible...

@interface NSString (wstring_additions)
+(NSString*) stringWithwstring:(const std::wstring&)string;
-(std::wstring) getwstring;
@end

@implementation NSString (wstring_additions)

#if TARGET_RT_BIG_ENDIAN
const NSStringEncoding kEncoding_wchar_t =
CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32BE);
#else
const NSStringEncoding kEncoding_wchar_t =
CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingUTF32LE);
#endif

+(NSString*) stringWithwstring:(const std::wstring&)ws
{
    char* data = (char*)ws.data();
    unsigned size = ws.size() * sizeof(wchar_t);

    NSString* result = [[[NSString alloc] initWithBytes:data length:size encoding:kEncoding_wchar_t] autorelease];
    return result;
}

-(std::wstring) getwstring
{
    NSData* asData = [self dataUsingEncoding:kEncoding_wchar_t];
    return std::wstring((wchar_t*)[asData bytes], [asData length] / sizeof(wchar_t));
}

@end
jbat100
  • 16,757
  • 4
  • 45
  • 70
  • The problem is that in most places I have no access to NSLog and NSString (it's mostly C++ code and it is supposed to be cross platform) I am going with the OSX implementation right now but it should remain cross platform. And having to deal with std::wstring now is a burden I have to bear until I find another solution. Currently I call MYLog(blahblah) which calls a extern C++ function which has access to Objective C. This is why I can't simply go with `NSLog(@"Received %@", [NSString stringWithwstring:d.name]);` – Coyote Nov 27 '11 at 22:46
1

Perhaps if you convert it to a CFStringRef using CoreFoundation and then pass that to either CFLog(..) or cast it to NSString* (toll free bridged) and use NSLog(...)

CFStrings are in CoreFoundation which is a C level API and so should be callable from non objc land.

<CoreFoundation/CFString.h> contains CFStringCreateWithBytes(...) or perhaps CFStringCreateWithCString(...) and passing in the appropriate CFStringEncoding

Dad
  • 6,388
  • 2
  • 28
  • 34
  • I will try to get rid of wstring ASAP... But in the meantime I will try your solution. Maybe with some macro I should be able to get away with it... – Coyote Nov 27 '11 at 23:34
  • I don't think you need to get rid of wstring. if you can include conditionally based on target (perhaps using a #define to flag when on iOS/MacOS) then just include core foundation header if set, then use #ifdef on the same define to have your logging function call the CF routines and CFLog(..) call. Remember to release the string if you create it with CFStringCreate... (using CFRelease) and check for NULL return from create routine (failure case), so you don't crash passing NULL to CFLog(..) or CFRelease(..). – Dad Nov 27 '11 at 23:39