5

Consider this short program that I wrote:

    #include <iostream>
    
    template<bool Debug = false>
    constexpr int add(const int& a, const int& b) { 
        if (Debug)
            std::cout << __FUNCTION__ << " called on line " << __LINE__ << '\n';
        return (a + b);
    }
    
    int main() {
        std::cout << add(3, 7) << '\n';
        std::cout << add<true>(5, 9) << '\n';
        return 0;
    }

It works just fine, and it gives the proper output:

10
add called on line 6
14

However, I'd like for the line number that is printed to be the line at the call site of the program which in this program should be line 12.

So how can I use __LINE__ or some other method to give me the line number from where the function was invoked?

The desired output would be:

10
add called on line 12
14

I would like for it to be generated from the function itself if possible.


-EDIT-

As a note to the reader, I'm open to any and all options but I am limited to C++17 for my current build environment and I'm using Visual Studio.

Waqar
  • 8,558
  • 4
  • 35
  • 43
Francis Cugler
  • 7,788
  • 2
  • 28
  • 59

2 Answers2

6

You can call it like this instead:

template<bool Debug = false>
constexpr int add(const int& a, const int& b, int loc = __LINE__) { 
    if (Debug)
        std::cout << __FUNCTION__ << " called on line " << loc << '\n';
    return (a + b);
}

int main() {
    std::cout << add(3, 7) << '\n';
    std::cout << add<true>(5, 9, __LINE__) << '\n';
    return 0;
}

Output:

10
add called on line 14
14

Furthermore, You can define a macro to skip the third argument:

#define add_Debug(a, b) add<true>(a,b,__LINE__)

C++ 20 and beyond

With C++20, we are getting std::source_location which contains information about line number, function, file name. If your compiler supports it, you can use that. Example (tested with g++ 9.3.0). You will not need macros anymore:

#include <iostream>
#include <experimental/source_location>

using source_location = std::experimental::source_location;

template<bool Debug = false>
constexpr int add(const int& a, const int& b, source_location l = source_location::current()) { 
    if (Debug)
          // this will print 'main' as function name
          //std::cout << l.function_name() << " called on line " << l.line() << //'\n';
        std::cout << source_location::current().function_name() << " called on line " << l.line() << '\n';


    return (a + b);
}

int main()
{
    std::cout << add(3, 7) << '\n';
    std::cout << add<true>(5, 9) << '\n';

    return 0;
}

Output:

10
add<true> called on line 16
14

With source_location you don't have to use macros anymore. It will correctly print the line

Waqar
  • 8,558
  • 4
  • 35
  • 43
  • Not exactly what I was asking for. The function call itself happens at line 12 in the code within the main function. That's the line number that I'm after. – Francis Cugler Jul 13 '20 at 05:51
  • I am adding more, bear with me :) – Waqar Jul 13 '20 at 05:54
  • This should work... however, having to add that into every function... ehh – Francis Cugler Jul 13 '20 at 05:54
  • It's for debug - unit testing and writing to log files though... – Francis Cugler Jul 13 '20 at 05:54
  • 1
    I hear that C++20 has a new proposed feature... can't wait to be able to start using it! – Francis Cugler Jul 13 '20 at 05:55
  • Hmm maybe add it into the `template arguments` instead of the function itself... – Francis Cugler Jul 13 '20 at 05:56
  • I tried using it as a template argument, setting the template argument as a default to `__LINE__` works, however, when I try to use `__LINE__` as the second parameter when instantiating the template, it does fail to compile because the `__LINE__` macro isn't considered a compile-time constant expression. – Francis Cugler Jul 13 '20 at 06:01
  • 1
    It does work, but the line information you will get will be incorrect unless you explicitly pass `__LINE__` like add(5, 9). With source_location, this is not an issue. You can just use it as a function parameter and it will print information of line which called the function – Waqar Jul 13 '20 at 06:12
  • I tried doing that `add(5,9)` but it was failing to compile for Visual Studio... – Francis Cugler Jul 13 '20 at 06:16
  • It's compiling for all compilers for me, see: https://godbolt.org/z/T91s5s – Waqar Jul 13 '20 at 06:22
  • 1
    Okay, I changed Compiler Explorer's settings to match my environment: x64 MSVC 19.24 and changed it to c++17. It compiled there fine. In Visual Studio 2017 when I compile it in Debug mode it is generating: `C2672` and `C2975` compile errors. However, when I switch to release mode, it compiles, builds, runs, and produces the correct output... Curious, I wonder if this is a Visual Studio Bug... – Francis Cugler Jul 13 '20 at 06:30
  • 1
    Could be a bug in MSVC. Cause there are no C++17 only features in that template function. – Waqar Jul 13 '20 at 06:41
  • I just asked another question in regards to it being a potential bug! https://stackoverflow.com/q/62870536/1757805 However, I got a downvote from it already... – Francis Cugler Jul 13 '20 at 06:46
2

If you could compile your code on Linux using some recent GCC (in July 2020, that means GCC 10), you might compile with g++ -g -O -Wall then use Ian Taylor's libbacktrace (like we do in RefPerSys) since that library parses DWARF debug information.

You might either port that library to your operating system and debug info format, or find some equivalent.

Today, a cruder possibility could be to wrap some functions with macros. For example, instead of calling foo() with some void foo(void); you would have

extern void foo_at(const char*fileno, int lineno);
#define foo() foo_at(__FILE__,__LINE__);

Indeed, C++20 should add feature tests and source_location but you might need to wait a few months for a compiler supporting it. Consider compiling a recent GCC from its source code. AFAIK MinGW 64 runs on Windows (so ask permission to use it).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547