0

I have a preprocessor macro that I use like this:

@implementation MyClass
- (void)methodA
{
    MyLog("Hello, method A!");
}

- (void)methodB
{
    MyLog("Hello, method %@!", @"B");
}
@end

And after macro expansion it looks like:

@implementation MyClass
- (void)methodA
{
    ; NSLog([NSString stringWithFormat:@"%s - %@", __func__, "Hello, method A!"]); ;
}

- (void)methodB
{
    ; NSLog([NSString stringWithFormat:@"%s - %@", __func__, "Hello, method %@!"], @"B"); ;
}
@end

This will result in these being printed for their respective methods:

-[MyClass methodA] - Hello, method A!
-[MyClass methodB] - Hello, method B!

I want to change this to a set of Objective-C or Swift methods, which I can call the same way and would give me the same result. I don't want to manage an object, so these should be class/static methods. Is it possible to tell that I'm in a particular class/method and only use a particular log prefix while in there?

If not, is there any other way to use methods to mimic the behavior I achieved with the macros?

Ky -
  • 30,724
  • 51
  • 192
  • 308
  • 1
    try looking **[here (objective-c)](http://stackoverflow.com/questions/2687785/any-way-to-ask-a-method-for-its-name/2687873#2687873)**, you should find what you are looking for (unless i misunderstood). You could define macros to to this automagically depending on your build context (debug vs distro). – YvesLeBorg May 18 '16 at 18:57
  • What the hell is with the semi colons? Don't do that. – Alexander May 18 '16 at 19:01
  • 1
    @AMomchilov those are Due to the fact that the macros have, within them, a semicolon before and after their statement (to guard against expansion syntax mishaps; the same reason `do { ... } while(0)` is used). So, when I type out the macro with a semicolon after it, all three appear in the expanded form. – Ky - May 18 '16 at 19:19
  • @YvesLeBorg Seems to just print out the log method's name :P – Ky - May 18 '16 at 19:21
  • @YvesLeBorg correct, but with the nice, terse expression I was able to achieve with the preprocessor macros – Ky - May 18 '16 at 19:27
  • Oh I wasn't aware that code was after marcro expansion, my bad – Alexander May 18 '16 at 20:07
  • 1
    Maybe useful [`NSThread.callStackSymbols `](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSThread_Class/#//apple_ref/occ/clm/NSThread/callStackSymbols) – Bryan Chen May 18 '16 at 21:49

2 Answers2

2

Generally the Swift tool you want here is #function. For example:

func MyLog(msg: String, function: StaticString = #function) {
    print("\(function) - \(msg)")
}

#function, when used as a default parameter value, evaluates to the function that at the calling site. Since it's a default value, you don't have to pass a function parameter.

This is not compatible with ObjC, however. For that, you'll still need macros. If you want to forward along an ObjC macro to Swift, you'd do something like:

#define MYLog(message) [Log log:message function:@(__FUNCTION__)]

You'd then need to write MyLog this way:

struct Log {
    static func log(msg: String, function: String = #function) {
        // ...
    }
}

It has to be in a struct to be accessible to ObjC (or it could be an enum), and you have to make function of type String rather than StaticString because ObjC can't generate StaticString.

Build like this, in Swift, you would call:

Log.log("my message")

and in ObjC you would call

MyLog("my message")
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

I produce (by macro expansion) a similar result.

In GERuntimeConstants.h, i define

extern void QuietLog(NSString *format, ...);

in GERuntimeConstants.m, i provide QuietLog as :

void QuietLog(NSString *format, ...) {

    if (format == nil) {
        printf("nil\n");
        return;
    }
    // Get a reference to the arguments that follow the format parameter
    va_list argList;
    va_start(argList, format);
    // Perform format string argument substitution, reinstate %% escapes, then print
    NSString *s = [[NSString alloc] initWithFormat:format arguments:argList];
    printf("%s\n", [[s stringByReplacingOccurrencesOfString:@"%%" withString:@"%%%%"] UTF8String]);
    va_end(argList);
}

in GEMacros, i define

#define __MPLOGWITHFUNCTION(s, ...) \
QuietLog(@"%s : %@",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__])  
#define MPLOG(...) __MPLOGWITHFUNCTION(__VA_ARGS__)

I include GEMacros.h in my .pch

anywhere i want to log , i would have a single line of code that looks like the following example :

- (void)cleanupWithError:(NSString *)status message:(NSString *)message {
      MPLOG(@"*** Cleaning up , status[%@], message[%@]",status,message);

and this statement translates to this in the console :

-[EHRCall cleanupWithError:message:] : *** Cleaning up , status[INVALID_PARAMETERS], message[No api user with api key [patient01ApiKey]]

So in my code, i use MPLOG as NSLog (always). The definition via _MPLOGWITHFUNCTION is because I have other macros that are tuned to the build. This definition is for logging while working a debug build. For distro MPLOG defines down to ;.

YvesLeBorg
  • 9,070
  • 8
  • 35
  • 48
  • ah, missed that ... i wont debate that for sure. I am lazy by nature, and like to be able to change one compilation flag to flip some of my `throw-away` code (like logging). – YvesLeBorg May 18 '16 at 19:49
  • That certainly is a wonderful feature of C. However, we're trying to move our code to be Swift-compatible, if not just Swift. – Ky - May 18 '16 at 20:00