Как получить номер линии с сайта вызова с помощью __LINE__ или другого метода?

Рассмотрим эту короткую программу, которую я написал:

    #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

Я хотел бы, чтобы он был сгенерирован из самой функции, если это возможно.


-EDIT-

В качестве примечания для читателя: я открыт для любых вариантов, но я ограничен C++17 для моей текущей среды сборки, и я использую Visual Studio.


person Francis Cugler    schedule 13.07.2020    source источник


Ответы (2)


Вместо этого вы можете называть это так:

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__)

С++ 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 вам больше не нужно использовать макросы. Он будет правильно печатать строку

person Waqar    schedule 13.07.2020
comment
Не совсем то, о чем я просил. Сам вызов функции происходит в строке 12 кода внутри основной функции. Это номер строки, который мне нужен. - person Francis Cugler; 13.07.2020
comment
Я добавляю больше, терпите меня :) - person Waqar; 13.07.2020
comment
Это должно работать... однако, нужно добавить это в каждую функцию... эхх - person Francis Cugler; 13.07.2020
comment
Это для отладки - модульного тестирования и записи в файлы журнала... - person Francis Cugler; 13.07.2020
comment
Я слышал, что в C++20 есть новая предлагаемая функция... не могу дождаться, когда смогу начать ее использовать! - person Francis Cugler; 13.07.2020
comment
Хм, может быть, добавить его в template arguments вместо самой функции... - person Francis Cugler; 13.07.2020
comment
Я пытался использовать его в качестве аргумента шаблона, установка аргумента шаблона по умолчанию на __LINE__ работает, однако, когда я пытаюсь использовать __LINE__ в качестве второго параметра при создании экземпляра шаблона, он не компилируется, потому что макрос __LINE__ не рассматривается постоянное выражение времени компиляции. - person Francis Cugler; 13.07.2020
comment
Это работает, но информация о строке, которую вы получите, будет неверной, если вы явно не передадите __LINE__, например add‹true, __LINE__›(5, 9). С source_location это не проблема. Вы можете просто использовать его как параметр функции, и он будет печатать информацию о строке, которая вызвала функцию. - person Waqar; 13.07.2020
comment
Я пытался сделать это add<true,__LINE__>(5,9), но мне не удалось скомпилировать его для Visual Studio... - person Francis Cugler; 13.07.2020
comment
У меня он компилируется для всех компиляторов, см.: godbolt.org/z/T91s5s - person Waqar; 13.07.2020
comment
Хорошо, я изменил настройки Compiler Explorer, чтобы они соответствовали моей среде: x64 MSVC 19.24 и изменил их на c++17. Он скомпилировался там нормально. В Visual Studio 2017, когда я компилирую его в режиме отладки, он генерирует: ошибки компиляции C2672 и C2975. Однако, когда я переключаюсь в режим выпуска, он компилируется, строится, запускается и выдает правильный вывод... Любопытно, интересно, это ошибка Visual Studio... - person Francis Cugler; 13.07.2020
comment
Может быть ошибка в MSVC. Потому что в этой функции шаблона нет только функций С++ 17. - person Waqar; 13.07.2020
comment
Я только что задал еще один вопрос относительно того, что это потенциальная ошибка! stackoverflow.com/q/62870536/1757805 Однако я уже получил отрицательный отзыв... - person Francis Cugler; 13.07.2020

Если бы вы могли скомпилировать свой код в Linux, используя недавний GCC (в июле 2020 г., это означает GCC 10), вы можете скомпилировать с g++ -g -O -Wall, затем используйте Яна Тейлора. libbacktrace (как мы делаем в RefPerSys), так как эта библиотека анализирует DWARF отладочная информация.

Вы можете либо перенести эту библиотеку на свою операционную систему и формат отладочной информации, либо найти эквивалент.

Сегодня более грубая возможность заключалась бы в том, чтобы обернуть некоторые функции макросами. Например, вместо вызова foo() с некоторым void foo(void); у вас будет

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

Действительно, C++20 должен добавить функциональные тесты и source_location, но вам, возможно, придется подождать несколько месяцев, пока компилятор его не поддержит. Рассмотрите возможность компиляции последнего GCC из его исходного кода. Насколько я знаю, MinGW 64 работает в Windows (поэтому спросите разрешения на его использование).

person Basile Starynkevitch    schedule 13.07.2020
comment
Да, но это своего рода откат к программированию на C, я пытаюсь раздвинуть границы C++ и двигаться вперед! - person Francis Cugler; 13.07.2020