views:

122

answers:

5

I am creating a logging facility for my library, and have made some nice macros such as:

#define DEBUG  myDebuggingClass(__FILE__, __FUNCTION__, __LINE__)
#define WARING myWarningClass(__FILE__, __FUNCTION__, __LINE__)

where myDebuggingClass and myWarningClass both have an overloaded << operator, and do some helpful things with log messages.

Now, I have some base class that users will be overloading called "Widget", and I would like to change these definitions to something more like:

#define DEBUG  myDebuggingClass(__FILE__, __FUNCTION__, __LINE__, this)
#define WARNING myWarningClass(__FILE__, __FUNCTION__, __LINE__, this)

so that when users call 'DEBUG << "Some Message"; ' I can check to see if the "this" argument dynamic_casts to a Widget, and if so I can do some useful things with that information, and if not then I can just ignore it. The only problem is that I would like users to be able to also issue DEBUG and WARNING messages from non-member functions, such as main(). However, given this simple macro, users will just get a compilation error because "this" will not be defined outside of class member functions.

The easiest solution is to just define separate WIDGET_DEBUG, WIDGET_WARNING, PLAIN_DEBUG, and PLAIN_WARNING macros and to document the differences to the users, but it would be really cool if there were a way to get around this. Has anyone seen any tricks for doing this sort of thing?

+2  A: 

Macros are basically a straight text substitution done by the preprocessor. There's no way for a macro to know the context from which it's being called to do the sort of detection you're interested in.

The best solution is probably separate macros as you suspect.

Mark B
A: 

The only way I can think of to possibly get this to work is to define a global variable:

Widget * this = NULL;

If that even compiles (I have my doubts, but don't have a compiler to test it), member functions will use the nearest scoped variable (the real his pointer), and everything else will get a null. Everyone's happy (so to speak...)

James Curran
It won't compile.
DeadMG
+1  A: 

I don't think you can do this with a macro. You can probably manage to do it with SFINAE, but code that uses SFINAE (at least directly) is1 hard to write, harder to debug, and virtually impossible for anybody but an expert to read or understand. If you really want to do this, I'd try to see if you can get Boost enable_if (or a relative thereof) to handle at least part of the dirty work.

1 ...at least in every case I've ever seen, and I have a hard time imagining it being otherwise either.

Jerry Coffin
Except that by using enable_if you ARE writing SFINAE reliant code, which sort of counters your argument since using enable_if is fairly easy. It's not like enable_if hides this fact either since just using enable_if isn't enough, you have to try to access its ::type field and you have to do so in such a way that triggers SFINAE. In reality though, I don't think SFINAE will work since 'this' must be accessed and when its not available that's not a SFINAE issue. I could be wrong but I don't see a way around this issue.
Noah Roberts
@Noah: Perhaps you should read a bit more carefully, looking with care at the "(at least directly)" part there. `enable_if` allows you to use it *indirectly*. I'll admit I'm not *sure* SFINAE will work for this either (which is why I said "probably") but I'm still not sure it can't.
Jerry Coffin
Well, as I said, enable_if doesn't allow you to use SFINAE indirectly. You are actually using it directly when you call enable_if. There's no SFINAE anything in the enable_if metafunction; it's just a trivial struct. All of the SFINAE is done at the call site by the person writing the client code. enable_if is just a nice, explanatory name that makes the purpose of the SFINAE code easier to understand. It is no different though than passing <code>int</code> to <code>template <typename T> T::type f();</code>
Noah Roberts
+2  A: 

Declare a global Widget* const widget_this = NULL; and a protected member variable widget_this in the Widget class, initialized to this, and do

#define DEBUG myDebuggingClass(__FILE__, __FUNCTION__, __LINE__, widget_this)

This ought to work.
Jon Purdy
I should note this answer is evil in a number of respects. It uses macros, which I dislike, variable shadowing, which I find distasteful, and protected data members, which I generally avoid (though I don't object here). This solution is the quickest and most direct answer to the question you asked, but that doesn't mean it is the answer your problem.
+1  A: 

Inspired by solipist, but slightly simpler in the implementation:

class Widget { 
  protected: 
  ::myDebuggingClass myDebuggingClass(char const* file, char const* function, int line) {
    return ::myDebuggingClass(file, function, line, this);
  }
  // ...

This eliminates the need for a shadowed variable; it relies on simple class name lookup rules.

MSalters