c++debuggingc-preprocessor

Use macros to implement a for statement with its own loop counter in C++


My requirement is to count the total number of times each for statement in the program loops during the entire runtime of the program, for example:

int for_counter_1 = 0;
int for_counter_2 = 0;

void f() {
    for (int i = 0; i < 10; i++, for_counter_2++) {
        // ...
    }
}
int main() {
    for (int i = 0; i < 10; i++, for_counter_1++) {
        f();
    }
    printf("%d\n", for_counter_1); // should output: 10
    printf("%d\n", for_counter_2); // should output: 100
    
}

Since there is a very large number of for loops in my program (some cryptographic algorithms), and I will continue to extend it, I thought about using macros to implement for statements with their own loop counters, like this:

#define CONCAT_INNER(a, b) a ## b
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define FOR_COUNTER() CONCAT(for_counter_, __LINE__)
#define for_with_counter(...) \
    static int FOR_COUNTER() = 0; \
    for (__VA_ARGS__, ++FOR_COUNTER())

void f() {
    for_with_counter(int i = 0; i < 10; ++i) {
        // ...
    }
}
int main() {
    for_with_counter(int i = 0; i < 10; ++i) {
        f();
    }
}

It can generate a static counter for_counter_N for each for loop, where N represents the line number where the loop is located. I can see the total number of times this loop has occurred in the debugging environment. However, since they are local static variables, I can't print them all out from the main function at the end of the program.

Is there any way to accomplish my purpose? It doesn't have to be a macro, and since I'm using C++, it would be fine if I could use templates and such to get around it. Or if there is a debugging tool that can do this that would also work.

Edit:

I'm sorry I didn't specify, it's not like I didn't think about using map or unordered_map, but since the performance requirements of for loops in my program are quite demanding (since I'm doing some work on cryptographic algorithm optimization), using these data structures would have some performance impact.


Solution

  • Whenever possible I try to stay away from macros completely. In this case I would make a type that behaves like an int but keeps track on how often it was incremented. For this demo (https://onlinegdb.com/zTJqjvNvE) the reporting is in the destructor (so when the counter goes out of scope).

    My line of thought, you don't need to know about the kind of loop just how often something is incremented (this approach will also work for while loops)

    #include <string>
    #include <iostream>
    
    template<typename type_t>
    class counter_t
    {
    public:
        explicit counter_t(const std::string name) :
            m_name{ name },
            m_count{ 0ul }
        {
        }
    
        auto& operator=(const type_t& value)
        {
            m_value = value;
            return *this;
        }
    
        void operator++()
        {
            m_value++;
            m_count++;
        }
    
        void operator++(int)
        {
            ++m_value;
            ++m_count;
        }
    
        operator const type_t& () const
        {
            return m_value;
        }
    
        operator type_t& ()
        {
            return m_value;
        }
    
        ~counter_t()
        {
            std::cout << "counter : `" << m_name << "` was incremented " << m_count << " times\n";
        }
    
    private:
        std::string m_name;
        type_t m_value;
        std::size_t m_count;
    };
    
    int main()
    {
        {
            counter_t<int> counter1{ "counter1" };
    
            for (counter1 = 1; counter1 < 10; ++counter1)
            {
                if (counter1 > 5) break;
            }
        } // go out of scope and report
    
        return 1; // can set breakpoint here
    }