Intro for sci-fi / horror movie

A gang of friends are walking in a city. One of the friends drops a little behind the others, and sees some kids lying back down just next to each other on a patch of grass (in a park or similar). A small ball (baseball?) comes rolling and cuts straight through the kids bodies. Some mysterious men come and wrap the remains of the kids bodies in some kind of plastic and hurls them up into the sky, and the bags just whooshes of with extremely high speed, and the men disappear. All in all it takes just a couple of seconds. The guy is shocked, and runs towards his friends. Then he see a football (soccer ball) come rolling toward his friends. He shouts to them to not touch it. They look at him and laugh, as one of the friends stops it with his foot. Unharmed. A second ball comes rolling and a second friend laughs and stops it as well, but the ball just continues straight through cutting of the leg (foot disappears). As the friend falls over screaming a couple of mysterious men runs over, wraps the stump in the plastic-like material, and grabs the friend throwing him into the air where he just speeds of. Then the men runs away just as quickly as they came. Again it’s all over in a couple of seconds.

Simple function-call tracing in C++

There are many ways to do function-call tracing in C++, where the simplest method is to just print "Entering function X" when entering the function, and right before returning print "Leaving function X". It’s a lot of work though, especially if the function have multiple return statements.

One solution to the problem above is to use a trace object. You create a class whose constructor prints the entering message, stores the function name, and whose destructor prints the leaving message. Then create an instance of this class as a local variable. When the object is instantiated, which happens when the function is called, the enter message will be printed. And when the function leave, no matter when or where or how it happens, the object will be destructed and the leave message will be printed.

Now how should such a class look like? It could be something simple like this:

struct tracer
{
    std::string name_;  // Name of the function

    tracer(std::string const& name)
        : name_(name)
    {
        std::clog << "Entering function " << name_ << std::endl;  // Flushing is important
    }

    ~tracer()
    {
        std::clog << "Leaving function " << name_ << std::endl;  // Flushing is still important
    }
};

That’s the basic of what is needed.

Now to use it is very simple:

void some_class::some_member_function()
{
    tracer _unique_name_{"some_class::some_member_function");

    // Lots of code and multiple `return` statements...

}

That is the basic usage. It has some drawbacks, in that tracing will always be enabled. It’s seldom needed for release builds for example, so only using it when a _DEBUG macro is defined is a good start. It might be even better to have a special TRACING_ENABLED macro so it can be enabled even in release builds which can be useful some times. Extra logic could also be added to check for flags set at run-time.


Here is an example full solution that uses preprocessor macros to enable and disable tracing at the time of compilation.

#pragma once
#ifndef TRACING_H
#define TRACING_H

#include <string>
#include <iostream>
#include <iomanip>

// Simple structure to handle function-call tracing.

// On debug builds, always build with tracing enabled unless explicitly disabled
#if defined(_DEBUG) && !defined(TRACING_DISABLED)
# define TRACING_ENABLED
#endif

// Define a preprocessor macro to help with the tracing
#ifdef TRACING_ENABLED
# define TRACE() tracing::tracer _tracer_object__ ## __COUNTER__ {__func__, __FILE__, __LINE__}
#else
# define TRACE() // Nothing
#endif

#ifdef TRACING_ENABLED
namespace tracing
{
    class tracer
    {
    public:
        tracer() = delete;  // Disallow default construction
        tracer(tracer const&) = delete;  // Disallow copy construction
        tracer(tracer&&) = delete;  // Disallow move construction
        tracer& operator=(tracer const&) = delete;  // Disallow copy assignment
        tracer& operator=(tracer&&) = delete;  // Disallow move assignment

        tracer(std::string const& fun, std::string const& file, int const line)
            : function_name{fun}, file_name{file}, line_number{line}
        {
            std::clog << "TRACE: Entering function " << function_name << " (" << file_name << ':' << line_number << ')' << std::endl;
        }

        ~tracer()
        {
            std::clog << "TRACE: Leaving function " << function_name << std::endl;
        }

    private:
        std::string function_name;
        std::string file_name;
        int         line_number;
    };
}
#endif // TRACING_ENABLED

#endif // TRACING_H

Sample program using the above header file:

#include "tracing.h"

struct foo
{
    int bar(int value)
    {
        TRACE();

        if (value < 10)
            return value * 2;
        else
            return value * 3;
    }
};

int main()
{
    TRACE();

    foo my_foo;
    my_foo.bar(20);
    my_foo.bar(5);
}

The output from the program as shown above could be something like

TRACE: Entering function main (/home/X/Y/main.cpp:18)
TRACE: Entering function bar (/home/X/Y/main.cpp:7)
TRACE: Leaving function bar
TRACE: Entering function bar (/home/X/Y/Testing/main.cpp:7)
TRACE: Leaving function bar
TRACE: Leaving function main

Output may vary depending on the compiler being used.