3

I'm building a tree-based debug/logging system for C++.

Its "user interface" is a macro which passes user-defined message and call site information (file, line, object address) to special function which then performs logging.

The function uses the object address to group messages by object instance.

Currently it looks like this:

// in logging system header
#define msg (event_level, message) \
    do_logging_ (event_level, __FILE__, __LINE__, this, message)

...

// in code
msg (MSG_WARNING, "some text");

I want to ask, is there some uniform way (usable in the msg macro) to get NULL instead of this where this is not defined (global/static functions)?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
intelfx
  • 2,386
  • 1
  • 19
  • 32
  • In theory (as far as the compiler can tell) this code may also appear outside functions; expressions can appear at global scope when used in initializers of global variables, or even in-class for static const class members. – MSalters Jul 22 '11 at 13:00

2 Answers2

4

You can change your macro definition:

#define msg (event_level, message, THIS) \
    do_logging_ (event_level, __FILE__, __LINE__, THIS, message)

Usage:

msg (MSG_WARNING, "some text", this); // in member methods
msg (MSG_WARNING, "some text", NULL); // otherwise
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Alternatively, cut down redundancy on the caller site by providing two wrapper macro for both options that supply `this` or `NULL` to the three-argument version. Takes away a bit of freedom though, as you can't issue a message for a given object from the outside (e.g. `friend` functions) - but nobody said the three-argument version has to become private. –  Jul 22 '11 at 12:57
  • @delnan, I was thinking about your suggested solution also; but above one just keeps it simple. so posted. – iammilind Jul 22 '11 at 12:58
  • Seems this is the only way... Not so 'elegant' solution, but anyway thanks. – intelfx Jul 22 '11 at 13:49
1

I think this is not possible without modifying the code in your class you want to log. If you are willing to do that, however, you could derive all classes where you want logging functionality from a template like this one:

    template <typename T>
    class loggable_class
    {
    protected:
        T* get_this() { return static_cast <T*> (this); }
    };

For example:

    class A : public loggable_class<A>
    {
        ...
    };

An additional global definition of the function get_this() will be used in non-member functions:

    inline void* get_this()
    {
         return NULL;
    }

The logging macro would look like:

    #define msg (event_level, message) \
        do_logging_ (event_level, __FILE__, __LINE__, get_this(), message)
cschwan
  • 3,283
  • 3
  • 22
  • 32
  • To be fair, you'd rather use a `dynamic_cast` in the member function. You may have confusing issues with diamond inheritance though. – Matthieu M. Jul 22 '11 at 13:15
  • I've just tried it, but when used in static member functions, compiler apparently prefers `loggable_class::get_this()` over global `get_this()`. And fails due to call without object. – intelfx Jul 22 '11 at 13:20
  • Doing a dynamic cast to void * means you missed a bit of the function of a dynamic cast. You can't functionally dynamic_cast to anything without a vtable. – dascandy Jul 22 '11 at 13:25