views:

127

answers:

2

Heads up: This is a weird question.

I've got some really useful macros that I like to use to simplify some logging. For example I can do Log(@"My message with arguments: %@, %@, %@", @"arg1", @"arg2", @"arg3"), and that will get expanded into a more complex method invocation that includes things like self, _cmd, __FILE__, __LINE__, etc, so that I can easily track where things are getting logged. This works great.

Now I'd like to expand my macros to not only work with Objective-C methods, but general C functions. The problem is the self and _cmd portions that are in the macro expansion. These two parameters don't exist in C functions. Ideally, I'd like to be able to use this same set of macros within C functions, but I'm running into problems. When I use (for example) my Log() macro, I get compiler warnings about self and _cmd being undeclared (which makes total sense).

My first thought was to do something like the following (in my macro):

if (thisFunctionIsACFunction) {
  DoLogging(nil, nil, format, ##__VA_ARGS__);
} else {
  DoLogging(self, _cmd, format, ##__VA_ARGS__);
}

This still produces compiler warnings, since the entire if() statement is substituted in place of the macro, resulting in errors with the self and _cmd keywords (even though they will never be executed during function execution).

My next thought was to do something like this (in my macro):

if (thisFunctionIsACFunction) {
  #define SELF nil
  #define CMD nil
} else {
  #define SELF self
  #define CMD _cmd
}
DoLogging(SELF, CMD, format, ##__VA_ARGS__);

That doesn't work, unfortunately. I get "error: '#' is not followed by a macro parameter" on my first #define.

My other thought was to create a second set of macros, specifically for use in C functions. This reeks of a bad code smell, and I really don't want to do this.

Is there some way I can use the same set of macros from within both Objective-C methods and C functions, and only reference self and _cmd if the macro is in an Objective-C method?

edit more information:

thisFunctionIsACFunction is determined in a pretty rudimentary way (and I'm definitely open to improvements and suggestions). Basically it's this:

BOOL thisFunctionIsACFunction == (__PRETTY_FUNCTION__[0] != '-' && __PRETTY_FUNCTION__[0] != '+');

It's relying on the behavior of the compiler to prepend a '-' or '+' for instance and class methods on Objective-C objects. Anything else must be a C function (since C functions can't have names that begin with '-' or '+').

I understand that this check is technically a runtime check, since __PRETTY_FUNCTION__ gets replaced with a char*, and this is probably the major roadblock to my request for help.

A: 

You can use some sort of macro conditional:

#ifndef OBJECTIVE_C
  #define self 0
  #define _cmd ""
#endif

I'm not quite sure what you should define self and _cmd to, those were just guesses.

I'm not sure if there is a predefined macro that you can to check if you are compiling in Objective C so you may need to define OBJECTIVE_C manually as part of your build.

R Samuel Klatchko
Good idea. The only problem is that Objective-C is a strict superset of C, which means that ObjC and C can peacefully co-exist side-by-side. This means that most of my C functions will have `__OBJC__` #define'd.
Dave DeLong
+5  A: 

The preprocessor does all of its work before the actual code is parsed. The preprocessor cannot know whether a function is C or obj-C because it runs before the code is parsed.

For the same reason,

if (thisFunctionIsACFunction) {
  #define SELF nil
  #define CMD nil
} else {
  #define SELF self
  #define CMD _cmd
}
DoLogging(SELF, CMD, format, ##__VA_ARGS__);

cannot work - the #defines are processed before the compilation stage.

So, the code itself must contain a "runtime" check (though the compiler may optimise this out).

I would suggest defining something like

void *self = nil; //not sure about the types that
SEL _cmd = nil;   //would be valid for obj-c

at global scope; the C functions will "see" these definitions while the Objective-C methods will hopefully hide them with their own definitions.

Artelius
+1 Ha! You're a genius! I never would've thought to define `self` and `_cmd` at a global level! Thanks! (And it works perfectly; I just tried it.)
Dave DeLong