Блог пользователя CodingKnight

Автор CodingKnight, 3 года назад, По-английски

Hello Codeforces,

It is sometimes required in competitive programming contests, specially in long-time challenges when there is enough time to trace the program execution behavior at run-time before submitting it to the automatic judge, to track the passed arguments and return value of some C++ function(s). This blog presents a simple C++ tracer class for such purpose.

The following is the class declaration and implementation.

Tracer code

If the macro TRACE is defined before this tracer code, the following trace operations are enabled using Variadic Macros and Variadic Function Templates.

  1. tr_begin(...) macro call increments the trace depth, and then prints the passed arguments.
  2. tr(...) macro call prints the passed arguments without updating the trace depth.
  3. tr_end(...) macro call prints the passed arguments, decrements the trace depth, and returns the first argument. This call can be used as the return value of the traced function.

Each traced function should have a tr_begin(...) macro call at the beginning of the function block, a tr_end(...) macro call at the end of the function block, and zero or more tr(...) macro call(s) inside the function block.

If the TRACE macro is undefined before the tracer code, the aforementioned trace operations are disabled.

The following is an example program to test the functionality of the tracer by tracking the execution of the recursive algorithm for computing the factorial function $$$n!$$$.

Example

The following is the standard output printed results of the example program when the TRACE macro is defined.

Output

Your constructive comments and feedback are evermore welcome.

Update

  • The following are interesting related blogs, shared thankfully by other Codeforces members.

    1. darkkcyan, My CP debugging template, shared by jalsol.
    2. rachitiitr, C++ Trick — Easy Debugging / Logging, shared by Manan_shah.
  • The trace macros trace_call(...) and trace_ret(...) were renamed to tr_begin(...) and tr_end(...), respectively, and a third macro tr(...) was added. The value of the built-in macro __LINE__ was also passed to each tracer object public member function call so as to identify the location of the trace point inside the traced function.

  • The macro db() by darkkcyan was adopted. If the macro TRACE is defined, then db(arg) produces string(arg)+" = "+tracer.stringify(arg). Any variadic argument of the macro calls tr_begin(...), tr(...), or tr_end(ans,...) can be passed as either arg or db(arg)

The following is the GitHub archive of the tracer code.

tracer@GitHub

  • Проголосовать: нравится
  • +40
  • Проголосовать: не нравится

»
3 года назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

I suppose you can just print the arguments passed in and return value, so this only keeps track of recursion depth?

  • »
    »
    3 года назад, # ^ |
    Rev. 10   Проголосовать: нравится 0 Проголосовать: не нравится

    Yes, the following code should perform the same operation done by the tracer object in the factorial function example if the trace depth is ignored.

    int factorial(int n) {
    #ifdef TRACE
        cout << "factorial(" << n << ") called", cin.get();
    #endif // TRACE
        const int ans = n > 1 ? n*factorial(n-1) : 1;
    #ifdef TRACE
        cout << "factorial(" << n << ") return value = " << ans, cin.get();
    #endif // TRACE
        return n; }
    

    The main advantages of the tracer class are that the conditional compilation commands are implicitly written using the trace_call and trace_ret macros, and that more compact code in required to perform the same operation.

»
3 года назад, # |
Rev. 4   Проголосовать: нравится 0 Проголосовать: не нравится

Thank to CodingKnight for blog,

For those who are having problem with new line (in my case Sublime Text 3) :

Replace : header(++trace_depth,fun,args...), pause("called"); }

With this : header(++trace_depth,fun,args...), pause("called\n"); }

Replace : header(trace_depth--,fun,args...), pause("return value = ",ans);

With this : header(trace_depth--, fun, args...), pause("return value = ", ans), pause("\n");

1.Also no need to add header files just use your bits header in place of those.

(You can also delete this line)

inline std::istream& skip_endl(std::istream &is) {
    return is.ignore(std::numeric_limits<std::streamsize>::max(),'\n'); }

2.May be by seeing his example it looks little complex, let me make easier for you :

  • whenever you want to print return : trace_ret(ans,n)
  • whenever you want to print call : trace_call(n)

and write your code as you wrote your before.it will perfect and when u have to debug just comment out #LOCAL

int func(int n)
{
    if (n < 2)
        return trace_ret(1, n);
    trace_call(n);
    return trace_ret(n*func(n-1), n);
}

void solve()
{
    int n;
    cin >> n ;
    cout << func(n);
}

For people who don't like lengthy codes and using sublime text 3 :

Code Folding :

Select the code and press:

Ctrl + Shift + [ to fold

Ctrl + Shift + ] to unfold

(It's a GIF , please click on image to view how it works :))

  • »
    »
    3 года назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Thanks for the constructive feedback. Yes, it is surely possible to add new-line character and to use two pause calls inside the tracer_t::ret function. I assumed that the tracer code is placed at the beginning of the program code. But it is surely possible to remove the header files and all the std:: prefixes from the tracer code if it is placed after the convenient competitive programming first couple of C++ lines:

    #include <bits/stdc++.h>
    using namespace std;
    
»
3 года назад, # |
  Проголосовать: нравится +11 Проголосовать: не нравится

This from darkkcyan can do somewhat the same thing, but much simpler to implement, so it's superior to me.