Создаем проект

Попробуем чего-нибудь написать на C++, создадим папку

mkdir cpp01
cd $_

создадим простой файлик на C++, какой-нибудь hello-world вот так

nano hello.cpp

и загоним туда такой код:

#include <iostream>
using namespace std;

int main()
{
    cout << "Hello World" << endl;
    return 0;
}

попробуем его скомпилировать

g++ hello.cpp -o hello

в папке появится зеленый файлик hello

попробуем его запустить

./hello

и увидим

Красота =)

Обрабатываем аргумент командной строки

Попробуем теперь поработать с командной строкой, чтобы можно было передать какой-нибудь параметр программе, и она на него среагировала.

Начнём с простого. Пусть наша программа здоровается с пользователем, а само имя пользователя будем передавать в качестве параметра

./hello "Вася"

очевидно, что если сейчас запустить программу, то никакого «привета» мы не увидим:

Надо как-то получить доступ к параметрам. Как известно лежат они в аргументах функции main Перепишем код следующим образом

#include <iostream>
using namespace std;

// argc – количество параметров, argv – значения параметров
int main(int argc, char* argv[])
{
    cout << argv[0] << endl; // вывести первый аргумент командной строки
    return 0;
}

перекомпилируем

g++ hello.cpp -o hello

и запускаем

хм, почему-то не вывел Васю

дело в том что первый аргумент командной строки это всегда название программы, которую мы запустили.

Мы запускали ./hello вот он его и написал.

Окей, давайте тогда выведем второй параметр

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    cout << argv[0] << endl;
    cout << argv[1] << endl;
    return 0;
}

проверим:

от теперь другое дело =)

Теперь попробуем сделать чтобы выводилось приветствие

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    cout << "Привет, " << argv[1] << endl;
    return 0;
}

проверяем

красота! =)

Работаем с числовым параметром

Добавим еще чтобы он мог рассчитать возраст Васи, то есть мы будем передавать ему год рождения в качестве параметра, а он нам будет считать.

Напишем пока просто такой код, чтобы год выводил

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    cout << "Привет, "
         << argv[1] << endl
         << "ты родился в " << argv[2] << " году" << endl;
    return 0;
}

проверим

отлично!)

Теперь, попробуем посчитать. Для этого надо преобразовать строку в число. Воспользуемся функцией atoi

Получится такой код:

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    int year = atoi(argv[2]); // преобразую в число
    cout << "Привет, "
         << argv[1] << endl
         << "ты родился в " << year << " году" << endl; // тут теперь переменную year подставил
    return 0;
}

работать должно так же:

теперь вытащим текущий год, к сожалению, в C++ это не очень веселая процедура но уж какая есть

#include <iostream>
using namespace std;

int main(int argc, char* argv[])
{
    int year = atoi(argv[2]);
    
    // берем текущее год
    auto t = time(nullptr);
    auto lt = localtime(&t);
    int currentYear = lt->tm_year + 1900;
    
    // считаем возраст
    int age = currentYear - year; 

    cout << currentYear << endl;
    
    // добавили вывод возраста
    cout << "Привет, "
         << argv[1] << endl
         << "ты родился в " << year << " году" << endl
         << "тебе " << age << " лет" << endl;
    return 0;
}

тестируем

а что будет если поменять параметры местами?

что удивительно, он сработал, но как мы видим он проинтерпретировал имя Вася как возраст, сконвертил его в 0, и получился очень странный ответ.

Именованные параметры

Вообще при разработке приложение очень часто приходится передавать параметры командной строки. Их количество может быть очень большим, десятки параметров вполне обычное дело, и понятно что запомнить их расположение весьма проблематично.

Чтобы бороться с этой проблемой придумали передавать именованные параметры (или как их еще называют флаги).

То есть идея такая вы перед каждым параметром пишете его имя примерно так:

./hello --year 2005 --name "Вася"

и теперь уже на важно в каком порядке вы их передадите, то есть следующий запуск должен сработать также:

./hello --name "Вася" --year 2005 

Но как можно догадаться, что если сейчас попробовать это запустить, оно не сработает

Шо же делать?

К счастью для нас, нет необходимости реализовывать эту логику самостоятельно.

Так как задача работы с параметрами очень распространённая, то все уже написано до нас. Осталось только найти нужную библиотечку

Мы воспользуемся либой, которая называется CLI11

Работаем с CLI11

Идем на github https://github.com/CLIUtils/CLI11/releases и качаем файлик

в принципе, можно скопировать ссылку

и как модный хакер, запустить в консольке команду

wget <скопированная ссылка>

увидим такую крипоту

но зато если написать ls -la то в папке заметим скачанный файлик:

И так, что за это файлик такой? Это обыкновенный C++ файл с кодом для обработки параметров командной строки

Попробуем ей воспользоваться, удалим весь старый код из hello.cpp и напишем

#include <iostream>
#include "CLI11.hpp"
using namespace std;

int main(int argc, char* argv[])
{
    // создаем описание нашего приложения
    CLI::App app{"Мое приложение"};

    // создаем переменную под имя
    // со значением по умолчанию "странник"
    std::string userName = "странник";
    // объявляем параметр командной строки --name
    // который привязываем к переменной userName
    app.add_option("-n,--name", userName, "имя пользователя");    
    
    // запускаем обработчик командной строки
    CLI11_PARSE(app, argc, argv);
    
    // к этому моменту в userName будет то имя которое переда юзер
    cout << "Привет, " << userName << endl;
    return 0;
}

компилируем:

g++ hello.cpp -o hello 

можно заметить, что время компиляции заметно возросло.

Тестируем

  • смотрите, если запустить его без параметров, то он использует имя по умолчанию, то есть “странник”
  • если попробовать передать Васю по старинке, то он ругается, что нельзя передать Вася без имени
  • ну а если передать Васю с именованным параметром, то все сработает как надо =)

го делать задание =О

Задание

Доработать программу из подсказки, чтобы она выводила возраст юзера по году переданному через именованный параметр