1

Sorry, but I don't even know how exactly to ask this.

I want to generate a trace log with some of the methods or functions being run, but I don't want to write the command name in every method (I'm really lazy!).

Example:

My current code:

void doSomething() {
    TRACE("doSomething");
    // now do something!
    system("pause");
}

What I want to do:

void doSomething() {
    TRACE;
    // do something!
    system("pause");
}

Output expected (of both programs):

doSomething
Press any key to continue...

If you need me to be more clear, please let me know. I'll try to be as clear as possible.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 3
    I think you want `__func__`. There's an explanation and examples at the bottom of [this page](http://en.cppreference.com/w/cpp/language/function). If TRACE is a macro, then it can substitute text including `__FILE__`, `__LINE__` and/or `__func__` for use in the log. – Tony Delroy Jun 03 '14 at 00:46
  • @TonyD Thank you! That is exactly what I wanted! Please, post your comment as an answer and I'll accept it! – Vinícius Gobbo A. de Oliveira Jun 03 '14 at 00:49
  • 1
    @ViníciusGobboA.deOliveira: Also, because you're using g++, you might look into [`__PRETTY_FUNCTION__`](https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html) as well. – Bill Lynch Jun 03 '14 at 00:49
  • 1
    You might want to look into the `-finstrument-functions` command line flag to insert the tracing code automagically. – Jerry Coffin Jun 03 '14 at 01:06

1 Answers1

1

I'd start with something like this:

#define TRACE(message) TRACE_IMPL(__FILE__, __LINE__, __PRETTY_FUNCTION__, message)

void TRACE_IMPL(const char *file, int line, const char *function, const char *message) {
    ...
}

int main() {
    TRACE("help");
}

My next step, would be to change message to be a format string, and enable printf() style va_args on the Trace. Which would look something like:

#include <cstdio>
#include <stdarg.h>

#define TRACE(format, ...) TRACE_IMPL("File: %s Line: %d Function: %s Message: " format "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, ##__VA_ARGS__)

void TRACE_IMPL(const char *format, ...) {
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

int main() {
    TRACE("help");
    TRACE("Canary %d", 2);
}

Which would output:

[8:18pm][wlynch@watermelon /tmp] ./foo
File: foo.c Line: 14 Function: int main() Message: help
File: foo.c Line: 15 Function: int main() Message: Canary 2

You could also use C++ streams if you wanted:

#include <iostream>

#define TRACE LogImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__)

class LogImpl {
    public:
        LogImpl(const char *file, int line, char *function) {
            std::cout << "File: " << file << " Line: " << line << " Function: " << function << " Message: ";
        }

        ~LogImpl() {
            std::cout << "\n";
        }

        LogImpl(LogImpl const &) = delete;
        LogImpl & operator=(LogImpl const &) = delete;

        template <typename T>
        LogImpl & operator<<(T const & obj) {
            std::cout << obj;
            return *this;
        }
};

int main() {
    TRACE << "help";
    TRACE << "Canary " << 2;
}
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
  • `printf`? Much safer and more extensible to accept arguments to stream to an `iostream`. – Tony Delroy Jun 03 '14 at 01:04
  • @TonyD: That's fair. But then I'd probably toss this whole style, and just go with a Log object of some sort. If you'd like to write something like that up, I imagine that Vinícius would find it interesting. – Bill Lynch Jun 03 '14 at 01:06
  • A log object alone can't help capture the function name (nor file, line...), so this "style" is necessary. I've marked this as a duplicate so can't add another answer now! Idea's very simple anyway: `#define TRACE(MSG) do { log_stream << __func__ << ' ' << MSG << '\n'; } while (false)` or similar. `do`/`while (false)` is just there in case you want to later add to the compound statement (e.g. prefix `if (logging_enabled)`); it prevents breaking `if`/`else` clauses in the context of macro use - a common trick for macros. – Tony Delroy Jun 03 '14 at 01:11
  • @TonyD: Right, but then your formatting has become more complex, or you use an odd syntax of `TRACE("canary " << 2);` or `TRACE(boost::format("canary %1%") % 2);` – Bill Lynch Jun 03 '14 at 01:15
  • " or you use an odd syntax " - it's only odd until you get used to it ;-). – Tony Delroy Jun 03 '14 at 01:16
  • @TonyD: I've added to the post the syntax I'd probably use if I wanted an iostreams interface. – Bill Lynch Jun 03 '14 at 01:30
  • it's less flexible. What will you do if you want to add a check for a "logging enabled" flag? Check it in the constructor and on every call to `<<`? Note that to call `<<` - potentially just to throw the value away - the arguments first need to be evaluated and that can be slow - not desirable when logging's disabled; with my approach from a few comments up you don't have to evaluate any arguments in that scenerio. – Tony Delroy Jun 03 '14 at 01:48
  • Wow, this scalated quickly! The implementation I've been using is with `iostreams`. Not as fancy as the one your provided but worked very well so far. – Vinícius Gobbo A. de Oliveira Jun 03 '14 at 02:08
  • @ViníciusGobboA.deOliveira true - worked on enough corporate libraries making these decisions to know all the pros and cons aren't immediately obvious... sometimes the design decisions limit what you can do later without rewriting "client" code, so choose well if it's a big system you're implementing and/or code outside your own control will adopt your interface. Cheers – Tony Delroy Jun 03 '14 at 07:40