【问题标题】:How to get the line number from the call site using __LINE__ or some other method?如何使用 __LINE__ 或其他方法从呼叫站点获取行号?
【发布时间】:2020-11-02 07:01:35
【问题描述】:

考虑一下我写的这个小程序:

    #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;
    }

它工作得很好,它给出了正确的输出:

10
add called on line 6
14

但是,我希望打印的行号是程序调用站点的行,在这个程序中应该是第 12 行。

那么我如何使用__LINE__ 或其他方法来给我调用函数的行号?

期望的输出是:

10
add called on line 12
14

如果可能的话,我希望它由函数本身生成。


-编辑-

作为对读者的说明,我对任何和所有选项持开放态度,但我当前的构建环境仅限于 C++17,并且我正在使用 Visual Studio。

【问题讨论】:

    标签: c++ visual-studio-2017 macros preprocessor line-numbers


    【解决方案1】:

    你可以这样称呼它:

    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;
    }
    

    输出:

    10
    add called on line 14
    14
    
    

    此外,您可以定义一个宏来跳过第三个参数:

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

    C++ 20 及更高版本

    使用 C++20,我们得到std::source_location,其中包含有关行号、函数、文件名的信息。如果你的编译器支持它,你可以使用它。示例(使用 g++ 9.3.0 测试)。您将不再需要宏:

    #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;
    }
    

    输出:

    10
    add<true> called on line 16
    14
    

    有了source_location,您就不必再使用宏了。它将正确打印该行

    【讨论】:

    • 不完全符合我的要求。函数调用本身发生在主函数内代码的第 12 行。那是我要的行号。
    • 我听说 C++20 有一个新的提议功能......迫不及待想要开始使用它!
    • 它确实有效,但是除非您像 add(5, 9) 一样明确传递__LINE__,否则您将获得的行信息将不正确。使用 source_location,这不是问题。您可以将其用作函数参数,它将打印调用该函数的行的信息
    • 好的,我更改了 Compiler Explorer 的设置以匹配我的环境:x64 MSVC 19.24 并将其更改为 c++17。它在那里编译得很好。在 Visual Studio 2017 中,当我在调试模式下编译它时,它会生成:C2672C2975 编译错误。但是,当我切换到发布模式时,它会编译、构建、运行并产生正确的输出...很好奇,我想知道这是否是 Visual Studio 错误...
    • 可能是 MSVC 中的错误。因为该模板函数中没有仅 C++17 的功能。
    【解决方案2】:

    如果您可以使用最近的 GCC(在 2020 年 7 月,这意味着 GCC 10)在 Linux 上编译您的代码,您可以编译 with g++ -g -O -Wall 然后使用 Ian Taylor 的 libbacktrace(就像我们在RefPerSys) 因为该库会解析 DWARF 调试信息。

    您可以将该库移植到您的操作系统并调试信息格式,或者找到一些等效的。

    今天,一种更粗略的可能性可能是用宏包装一些函数。例如,不是用一些void foo(void); 调用foo(),而是

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

    确实,C++20 应该添加 feature testssource_location,但您可能需要等待几个月才能获得支持它的编译器。考虑从源代码编译最近的 GCC。 AFAIK MinGW 64 在 Windows 上运行(因此请获得使用权限)。

    【讨论】:

    • 是的,但这有点倒退到 C 编程,我正试图突破 C++ 的极限并向前迈进!
    猜你喜欢
    • 1970-01-01
    • 2014-01-23
    • 1970-01-01
    • 2012-06-07
    • 2014-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多