LINUX.ORG.RU

Кросс-платформенный разбор аргументов командной строки

 ,


2

4

Понадобилось разбирать аргументы командной строки на С++. Самое главное требование: чтобы можно было с минимумом усилий и зависимостей собирать на Linux, Windows и MacOS. Погуглил и понял, что в случае с С++ не все так просто, как в более современных ЯП, а именно есть такие варианты:

  1. Стародедовский копролит из POSIX, т.е. getopt. Минус: в стандартной поставке MS VC++ его, как я понял, нет, ибо unistd.h. Кто-то предлагает специально для оффтопика передирать часть unistd.h и соотв. функционала, но это звучит как исключительно красноглазый пердолинг;

  2. boost.program_options Минус: только ради этого добавлять зависимость от части boost…

  3. QCommandLineParser Минус: см. п.2), только в этом случае надо тащить часть Qt, что кажется еще большим оверкиллом;

  4. Какие-то костыли из рецептов «одной бабки» с SO. Минус: нет желания лепить самодельный велосипед на ровном месте, и еще надо понять, что код вообще рабочий, а не только лайкнут автором вопроса, который этот код запустил один раз и выбросил;

  5. Какие-то васянские библиотеки. Минус: см. п.4). Хоть качество (может быть) и выше, чем портянки с SO, все равно надо тянуть доп. зависимость, и тогда уж лучше взять boost как более качественную библиотеку.

Ничего не забыл? На сколько я понял, в С++20 этого тоже нет. Как-то грустно в 21-м веке, когда уже вот-вот скоро выйдет 6й по счету стандарт языка, не иметь такой фичи в стандартной библиотеке.

★★★★★

Пили велосипед. Если аргументов мало и их комбинации не сложны то я делаю в лоб.

Импульсивные эмоционалы - закройте глазки ^.^

#if defined(USE_ASSET_LIB_COMPILER)

#include "egnaroc.h"

#define   RED      31
#define   GREEN    32
#define   YELLOW   33
#define   BLUE     34
#define   PURPULE  35
#define   AQUA     36
#define   DEFAULT  39


#define _color_(color) "\x1B["#color"m"
#define colorin(color) _color_(color)
#define colored(color,message) colorin(color) message colorin(DEFAULT)


static void colorset(int color)
{
  printf("\x1B[%dm",color);
}

static void colorputs(int color,char * text)
{
    colorset(color);
    printf("%s",text);
    colorset(DEFAULT);
}



#define getopt(option_struct,argc,argv)                              \
{                                                                    \
                                                                     \
      char * value = NULL;                                           \
      if((value = strstr(argv[argc],option_struct.option)) != NULL)  \
      {                                                              \
         option_struct.detect = true;                                \
         if((option_struct.value = strchr(value,'='))){              \
         if(strlen(option_struct.value) > 1)                         \
         {                                                           \
            option_struct.value++;                                   \
         }}                                                          \
      }                                                              \
}

enum {OPTION_FILED = -69};

#define checkopt(option_struct,warning_message,stop) \
{                                                    \
    if(!option_struct.value)                         \
    {                                                \
        printf(warning_message);                     \
        if(stop)                                     \
        {                                            \
            return OPTION_FILED;                     \
        }                                            \
    }                                                \
}

void cli_debug(const char * msg)
{
    char * nmsg = strchr(msg,')');
    printf("[CLI]%s\n",nmsg+1);
}
void cli_error(const char * msg)
{
    char * nmsg = strchr(msg,')');
    printf("[CLI]%s\n",nmsg+1);
    abort();
}

typedef struct{
    const char * option;
    char       * value;
    bool         detect;
}asset_lib_compiler_options;

static asset_lib_compiler_options
alib_compiler    = {"--compiler" ,NULL,false},
alib_target      = {"--target"   ,NULL,false},
alib_archiver    = {"--archiver" ,NULL,false},
alib_libname     = {"--libname"  ,NULL,false},
alib_assets      = {"--assets"   ,NULL,false},
alib_test        = {"--test"     ,NULL,false},
alib_replace     = {"--replace"  ,NULL,false},
alib_workdir     = {"--workdir"  ,NULL,false},
alib_outdir      = {"--outdir"   ,NULL,false},
alib_help        = {"--help"     ,NULL,false};



void asset_lib_compiler_help()
{
   const char message_en[] = "\n"
   colorin(GREEN)
   " ============================================================================\n"
   " WIP!   EGNAROC Util  - Simple Asset lib builder for create static assets    \n"
   " ============================================================================\n"
   colorin(DEFAULT)
   " --compiler=  C language compiler                                            \n"
   " --target=    Target platform windows|linux|android|darwin|emscripten        \n"
   " --archiver=  Archiver for create static lib                                 \n"
   " --libname=   Lib name identificator for init                                \n"
   " --assets=    Directory raw assets fales                                     \n"
   " --test=      Test load lib after build,if set --test=libname run only test  \n"
   " --replace=   Replace --assets=path to newpath,'../data/file'->'assets/file' \n"
   " --workdir=   Set working directory                                          \n"
   " --outdir=    Set build library out location                                 \n"
   " --help       This help                                                      \n";
   printf("%s\n",message_en);
}

int main(int argc, char *argv[])
{
    at_warning(cli_debug);
    at_debug(cli_debug);
    at_error(cli_error);

    while(argc-- > 0)
    {
        getopt(alib_compiler,argc,argv);
        getopt(alib_archiver,argc,argv);
        getopt(alib_target  ,argc,argv);
        getopt(alib_assets  ,argc,argv);
        getopt(alib_libname ,argc,argv);
        getopt(alib_target  ,argc,argv);
        getopt(alib_help    ,argc,argv);
        getopt(alib_test    ,argc,argv);
        getopt(alib_replace ,argc,argv);
        getopt(alib_workdir ,argc,argv);
        getopt(alib_outdir  ,argc,argv);
    }

    if(alib_replace.value == NULL)
    {
        alib_replace.value = "";
    }

    if(alib_outdir.value == NULL)
    {
        alib_outdir.value = "";
    }

    if(alib_workdir.value == NULL)
    {
        alib_workdir.value = "";
    }

    if(alib_help.detect)
    {
        asset_lib_compiler_help();
        return 0;
    }

    if(alib_test.detect && alib_test.value)
    {
        asset_lib_init();

        bool   success = false;
        debug(colored(YELLOW,"======== START ONLY TEST LIB ========="));

        if(strcmp(SDL_GetPlatform(),"Windows") != 0)
        {
            fpath lib = asset_map_filename( P(alib_test.value));
            success = asset_lib_load(lib);
            asset_lib_unload(lib);
        }else 
        if(strcmp(SDL_GetPlatform(),"Windows") == 0)
        {
            fpath lib = asset_map_filename(P(alib_test.value));
            success = asset_lib_load(lib);
            asset_lib_unload(lib);
        }else{
            warning(colored(RED,"Current platform is '%s', '--test' option support only for window,linux,android,darwin"),SDL_GetPlatform());
            return 0;
        }
        asset_lib_quit();
        if(success)
        {
            debug(colored(GREEN,"======== TEST LIB SUCCESS! ========="));
        }else{
            debug(colored(RED,  "========  TEST LIB FILED!  ========="));
        }
        return 0;
    }

    checkopt(alib_compiler,colored(RED,"\nMy friend, need set --compiler=you_compiler   ! Use --help\n\n"),true);
    checkopt(alib_archiver,colored(RED,"\nMy friend, need set --archiver=you_archiver    ! Use --help\n\n"),true);
    checkopt(alib_target,  colored(RED,"\nMy friend, need set --target=you_target_system  ! Use --help\n\n"),true);
    checkopt(alib_libname, colored(RED,"\nMy friend, need set --libname=you_library_name_id ! Use --help\n\n"),true);
    checkopt(alib_assets,  colored(RED,"\nMy friend, need set --assets=you_path_to_assets    ! Use --help\n\n"),true);
    checkopt(alib_target,  colored(RED,"\nMy friend, need set --target=you_target_platform    ! Use --help\n\n"),true);

    current_asset_folder = P(alib_assets.value);
    replace_asset_folder = P(alib_replace.value);

    asset_lib_create(P(alib_assets.value),P(alib_workdir.value),P(alib_outdir.value),P(alib_libname.value),P(alib_target.value),P(alib_compiler.value),P(alib_archiver.value));

..... blablabla

Но для всех линуксов есть POSIX, для всех виндовсов есть mingw-w64 c POSIX причём удобно конпелять для виндовс из под линукс. Очень удобно особенно если не хочется поделие мелкомягких трогать и там собирать. Макось… там тоже есть всё нужное для юзания POSIX (поправьте если я не прав)

Как-то грустно в 21-м веке, когда уже вот-вот скоро выйдет 6й по счету стандарт языка, не иметь такой фичи в стандартной библиотеке.

Так argv/argc это и есть всё готовое! Как это обрабатывать дело десятое. Хотя удобства хочется всегда особенно в таких случаях по сути второстепенных.

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от fsb4000

8 пакетных менеджеров недостаточно?

Должен быть один. Ну и большая часть из них не умеет собирать нужную версию с нужными флагами.

RazrFalcon ★★★★★
()

Не вижу ничего плозого в бусте. Там много полезных вещей есть кроме program options, которые жизнь облегчают.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

vcpkg умеет, само собой придется взять и настроить сборку нужных пакетов своими силами, что б были нужные опции.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Умеет, но если «комьюнити» через одно место собирает пакет, то ничего не поделаешь.

invy ★★★★★
()
Ответ на: комментарий от RazrFalcon

Ну и большая часть из них не умеет собирать нужную версию с нужными флагами.

Как там в 2003?

anonymous
()
Ответ на: комментарий от SR_team

Это просто переключение синтаксиса.

А ты что хотел? Чтобы можно было программировать под wintel инструкциями для mips или что?

LamerOk ★★★★★
()

boost.program_options Минус: только ради этого добавлять зависимость от части boost…

Не могу сейчас найти, но где-то видел имплиментацию этой хрени в хедер-онли и оторванной от буста.

anonymous
()

python argparse + python embeded

anonymous
()
Ответ на: комментарий от LamerOk

хотеть

int a = ...
...
asm{
    mov eax, a;
    ret;
}

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

SR_team ★★★★★
()
Ответ на: комментарий от Harald

Не правильно выразился, он не считается дефолтом. на винде дефолт - убогий студийный кл.эхэ, не дающий использовать не инициализированные переменные

SR_team ★★★★★
()
Ответ на: комментарий от fsb4000

А стандарт разве запрещает?

Кстати, если рассматривать не компилятор в вакуме, то разработка в msvs - боль. winsock2.h конфликтует с windows.h и проект с winsock2.h не будет компилироваться пока дефайн специальный не добавишь. Но это я вин апи к компилю притянул. Давай притянем еще и студию: ошибки почти не подсвечивает, сказал бы, что варнинги не просвечивает вообще, но видел разу предупреждение про return в critical section (это системный __try .. __catch .. __final), подсказки при наведении есть не всегда (почему?), doxygen-комменты не поддерживает (есть аддон), с рефакторингом все очень плохо (есть платный аддон).

Когда-то мне говорили, что 17’ая студия хороша, но судя по 19’ой, разрабы либо решили, что 17’ая была слишком хороша и в 19’ой понерфили фичи, либо 17’ая была хуже 19’ой, и каким же говном тогда были более ранние версии, если 17’ой люди так восхищались?

SR_team ★★★★★
()
Ответ на: комментарий от SR_team

А стандарт разве запрещает?

нет, но это же UB.

C IntelliSense есть проблемы, он может и наоборот ошибки подсвечивать где их нет. Проблема в том что для IntelliSense они используют EDG фронтенд, и EDG и MSVC различаются по поддерживаемым фичам.

В Qt Creator это тоже есть, правда в меньшей степени, когда для разбора используется libclang, а компилируется gcc.

С рефакторингом может и хуже чем в IDEA зато отзывчивее.

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

С рефакторингом может и хуже чем в IDEA зато отзывчивее.

Сравниваю с QtCreator. в msvs рефакторинг хуже , по отзывчивочти тоже хуже, но это просто студия тяжёлая.

C IntelliSense есть проблемы, он может и наоборот ошибки подсвечивать где их нет. Проблема в том что для IntelliSense они используют EDG фронтенд, и EDG и MSVC различаются по поддерживаемым фичам.

пока вы мне отвечали я нашёл ещё одну проблему - IntelliSense захотелось.

https://nc.prime-hack.net/s/qR8TTwN89XLwgBX

На видео msvs делает вид, что хидер не подключен, но он есть и все компилится. Закрытие и открытие файла ещё раз проблему не исправляет, помогает перезапуск студии, НО после перезапуска студии может захотеться работать без истории изменений и ctrl+z/ctrl+y перестанет работать. Лечится так же перезапуском

SR_team ★★★★★
()
Последнее исправление: SR_team (всего исправлений: 2)
Ответ на: комментарий от SR_team

помогает перезапуск студии

Я был не прав, она просто 100 лет сканирует все, прежде чем раскрасить код. Нихера в такой ситуации не помогает

SR_team ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.