단일 헤더 파일 에 포함된 C++11/14/17에 대한 사용하기 쉽고 강력하며 표현력이 풍부한 명령줄 인수 처리.
옵션, 옵션+값, 위치 값, 위치 명령, 중첩된 대안, 의사 결정 트리, 결합 가능한 플래그, 사용자 정의 값 필터, ...
문서 생성(사용 줄, 매뉴얼 페이지); 오류 처리
많은 예; 대규모 테스트 세트
다음 명령줄 인터페이스를 고려해보세요.
개요 변환 <입력 파일> [-r] [-o <출력 형식>] [-utf16] 옵션 -r, --recursive 파일을 재귀적으로 변환합니다. -utf16은 UTF-16 인코딩을 사용합니다.
다음은 위치 값 input file
과 세 가지 옵션 -r
, -o
및 -utf16
정의하는 코드입니다. 구문 분석이 실패하면 위의 기본 매뉴얼 페이지와 같은 조각이 stdout에 인쇄됩니다.
#include <iostream>#include "clipp.h"사용 네임스페이스 Clipp; std::cout 사용; std::string;int main(int argc, char* argv[]) { bool rec = false, utf16 = false; string infile = "", fmt = "csv";auto cli = (value("입력 파일", infile),option("-r", "--recursive").set(rec).doc("파일 변환 재귀적으로"),option("-o") & value("출력 형식", fmt),option("-utf16").set(utf16).doc("사용 UTF-16 인코딩') );if(!parse(argc, argv, cli)) cout << make_man_page(cli, argv[0]);// ...}
개요 finder make <단어파일> -dict <사전> [--progress] [-v] finder find <infile>... -dict <사전> [-o <outfile>] [-split|-nosplit] [-v] 찾기 도움말 [-v] 옵션 --progress, -p 진행 상황 표시 -o, --output <outfile> stdout 대신 파일에 쓰기 -split, -nosplit(하지 않음) 분할 출력 -v, --version 버전 표시
이 CLI에는 세 가지 대체 명령( make
, find
, help
)이 있습니다. 일부 위치 값 인수( <wordfile>
, <infile>
) 중 하나는 반복 가능하며 값 인수가 있는 필수 플래그( -dict <dictionary>
), 값 인수가 있는 옵션( -o <outfile>
), 두 가지 대안이 있는 하나의 옵션( -split
, -nosplit
) 및 두 개의 기존 옵션( -v
, --progress
).
다음은 인터페이스를 정의하고 위의 매뉴얼 페이지 조각을 생성 하며 구문 분석 결과를 처리하는 코드입니다.
네임스페이스 Clipp 사용; std::cout 사용; std::string 사용;//파싱 결과를 저장하는 변수; 기본값으로 초기화되었습니다. enum 클래스 모드 {make, find, help}; 선택된 모드 = 모드::도움말; 표준::벡터<문자열> 입력; string dict, out;bool Split = false, progr = false;auto Dictionary = 필수("-dict") & value("dictionary", dict);auto makeMode = (command("make").set(selected,mode) ::만들다), 값("워드파일", 입력), 사전, option("--progress", "-p").set(progr) % "진행률 표시" );auto findMode = (command("find").set(selected,mode::find), 값("파일", 입력), 사전, (option("-o", "--output") & value("outfile", out)) % "stdout 대신 파일에 쓰기", ( option("-split" ).set(split,true) | option("-nosplit").set(split,false) ) % "(출력 분할 안 함)" );auto cli = ( (makeMode | findMode | command("help").set(selected,mode::help) ),option("-v", "--version").call([]{cout << "version 1.0nn" ;}).doc("버전 표시") );if(parse(argc, argv, cli)) {switch(selected) {case mode::make: /* ... */ break;case 모드::find : /* ... */ break;case mode::help: cout << make_man_page(cli, "finder"); 부서지다; } } 또 다른 { cout << Usage_lines(cli, "finder") << 'n'; }
다음은 Clipp의 작동 방식에 대한 아이디어를 제공하는 몇 가지 예입니다. 명령줄 인수를 사용하여 설정하려는 몇 가지 변수가 포함된 기본 설정을 고려해 보세요.
int main(int argc, char* argv[]) { 네임스페이스 Clipp 사용;// 일부 변수 정의bool a = false, b = false;int n = 0, k = 0;double x = 0.0, y = 0.0; std::Vector<int> ids;auto cli = ( /* 명령줄 인터페이스를 정의하는 코드는 여기에 표시됩니다 */ );parse(argc, argv, cli); //argv[0]std::cout 제외 << Usage_lines(cli, "exe") << 'n'; }
인터페이스( usage_lines ) | 코드( cli 괄호 내용) |
---|---|
exe [-a] | option("-a", "--all").set(a) |
exe [--all] | option("--all", "-a", "--ALL").set(a) |
exe [-a] [-b] | option("-a").set(a), option("-b").set(b) |
exe -a | required("-a").set(a) |
exe [-a] -b | option("-a").set(a), required("-b").set(b) |
exe [-n <times>] | option("-n", "--iter") & value("times", n) |
exe [-n [<times>]] | option("-n", "--iter") & opt_value("times", n) |
exe -n <times> | required("-n", "--iter") & value("times", n) |
exe -n [<times>] | required("-n", "--iter") & opt_value("times", n) |
exe [-c <x> <y>] | option("-c") & value("x", x) & value("y", y) |
exe -c <x> <y> | required("-c") & value("x", x) & value("y", y) |
exe -c <x> [<y>] | required("-c") & value("x", x) & opt_value("y", y) |
exe [-l <lines>...] | option("-l") & values("lines", ids) |
exe [-l [<lines>...]] | option("-l") & opt_values("lines", ids) |
exe [-l <lines>]... | repeatable( option("-l") & value("lines", ids) ) |
exe -l <lines>... | required("-l") & values("lines", ids) |
exe -l [<lines>...] | required("-l") & opt_values("lines", ids) |
exe (-l <lines>)... | repeatable( required("-l") & value("lines", ids) ) |
exe fetch [-a] | command("fetch").set(k,1), option("-a").set(a) |
exe init | fetch [-a] | command("init").set(k,0) | (command("fetch").set(k,1), option("-a").set(a)) |
exe [-a|-b] | option("-a").set(a) | option("-b").set(b) |
exe [-ma|b] | option("-m") & (required("a").set(a) | required("b").set(b)) |
각 주제에 대한 자세한 설명은 예제 섹션을 참조하세요.
가독성을 높이기 위해 모든 예에서 네임스페이스 한정자는 생략되었습니다. 모든 엔터티는 namespace clipp
에 정의됩니다.
int main(int argc, char* argv[]) { 네임스페이스 사용 Clipp;auto cli = ( /* 명령줄 인터페이스를 정의하는 코드는 여기에 표시됩니다 */ );parse(argc, argv, cli); //argv[0] 제외//argv[0]을 포함하려는 경우//parse(argv, argv+argc, cli);}
명령줄 인터페이스에는 매개변수와 그룹이라는 두 가지 종류의 빌딩 블록이 있습니다. 편리하게 명명된 팩토리 기능은 원하는 설정이 적용된 매개변수 또는 그룹을 생성합니다.
bool a = 거짓, f = 거짓; 문자열 s; vector<string> vs;auto cli = ( // 필수 위치 반복 가능 명령("push")과 일치, // 정확히 예 예 norequired("-f", "--file").set(f), // 정확히 예 no norequired("-a", "--all", "-A").set(a), // 정확히는 아니요 아니요 아니요 value("file", s), // 모든 인수 예 예 novalues("file", vs), // 모든 인수 예 예 yesopt_value("file", s), // 모든 인수 no yes noopt_values("file" , vs), // 모든 인수 no yes yes//"catch all" 매개변수 - 오류 처리에 유용합니다.any_other(vs), // 모든 인수 no no yes//술어를 충족하지만 그렇지 않은 인수를 포착합니다. 다른 매개변수와 일치any(predicate, vs) // 조건자 no no yes);
위의 기능은 편의 팩토리입니다.
bool f = 참; string s;auto v1 = 값("file", s);//는 다음과 동일합니다:auto v2 = 매개변수{match::nonempty}.label("file").blocking(true).repeatable(true).set (s);auto r1 = 필수("-f", "--file").set(f);//는 다음과 동일합니다:auto r2 = 매개변수{"-f", "--file"}.required(true).set(f);
필수 매개변수는 하나 이상의 명령줄 인수와 일치해야 합니다.
반복 가능한 매개변수는 임의 개수의 인수와 일치할 수 있습니다.
위치가 아닌(=비차단) 매개변수는 어떤 순서로든 인수와 일치할 수 있습니다.
위치(차단) 매개변수는 "중지 지점"을 정의합니다. 즉, 이 매개변수가 일치할 때까지 그 뒤의 모든 매개변수는 일치할 수 없습니다. 일단 일치하면 그 이전의 모든 매개변수(현재 그룹 내)에 접근할 수 없게 됩니다.
매개변수를 순서대로 일치시키려면 operator &
또는 in_sequence
그룹화 함수를 사용하여 매개변수를 묶을 수 있습니다.
int n = 1; 문자열 s; vector<int> ls;auto cli = (//필수 값이 있는 옵션option("-n", "--repeat") & value("times", n),//옵션 값이 있는 필수 플래그required("--file ") & opt_value("name", s), //정확히 두 개의 값을 갖는 옵션option("-p", "--pos") & value("x") & value("y"),//이전과 동일 v vin_sequence( option("-p", "--pos") , value("x") , value("y") ), //최소한의 옵션 하나의 값(및 선택적으로 그 이상)option("-l") & value("lines", ls) );
값 매개변수는 필터 함수를 사용하여 인수 문자열과 일치할 수 있는지 테스트합니다. value
, values
, opt_value
및 opt_values
에서 사용되는 기본 필터 match::nonempty
비어 있지 않은 인수 문자열과 일치합니다. value
, values
등의 첫 번째 인수로 다른 필터 함수/함수 개체를 제공하거나 가장 일반적인 경우를 다루는 다음 내장 단축 팩토리 함수 중 하나를 사용할 수 있습니다.
문자열 이름; 더블 r = 0.0; int n = 0;auto cli = (value("user", name), // 비어 있지 않은 문자열 단어("user", name)와 일치, // 비어 있지 않은 영숫자 문자열 번호("ratio", r)와 일치 , // 숫자의 문자열 표현과 일치합니다integer("times", n) // 정수의 문자열 표현과 일치);
value
, opt_value
등과 유사합니다. words
, opt_word
등에 대한 함수도 있습니다.
auto is_char = [](const string& arg) { return arg.size() == 1 && std::isalpha(arg[0]); };문자 c = ' '; // 필요한 위치 반복값과 일치합니다(is_char, "c", c); // 한 문자 예 예 아니오
괄호와 쉼표를 사용하여 상호 호환되는 매개변수를 그룹화합니다.
auto cli = ( option("-a"), option("-b"), option("-c") );
operator |
사용하여 상호 배타적인 매개변수를 대안으로 그룹화합니다. 또는 one_of
:
auto cli1 = ( value("input_file") | command("list") | command("flush") );auto cli2 = one_of( value("input_file") , command("list") , command("flush" ) );
operator &
또는 in_sequence
연산자를 사용하여 순서대로 일치해야 하는 매개변수를 그룹화합니다.
double x = 0, y = 0, z = 0;auto cli1 = ( option("-pos") & value("X",x) & value("Y",y) & value("Z",z ) );auto cli2 = in_sequence( option("-pos") , value("X",x) , value("Y",y) , value("Z",z) );
주변 그룹은 이에 의해 영향을 받지 않으므로 -a
와 -b
어떤 순서로든 일치할 수 있지만 -b
와 X
값은 순서대로 일치해야 합니다.
부울 a = 거짓, b = 거짓; int x = 0;auto cli = ( option("-a").set(a), option("-b").set(b) & value("X",x) );
그룹은 중첩되고 결합되어 임의로 복잡한 인터페이스를 형성할 수 있습니다(여기 및 여기 참조).
auto cli = ( command("push") | ( command("pull"), option("-f", "--force") ) );
그룹도 반복 가능합니다.
auto cli1 = 반복 가능( command("flip") | command("flop") );
플래그 그룹에 공통 접두사를 강제 적용합니다.
int x = 0;auto cli1 = with_prefix("-", option("a"), option("b") & value("x",x), ... ); // => -a -b ^unaffected^auto cli2 = with_prefix_short_long("-", "--", option("a", "all"), option("b"), ... ); // => -a --all -b
플래그 그룹에 공통 접미사를 강제로 적용합니다.
int x = 0;auto cli1 = with_suffix("=", option("a") & value("x",x), ... ); // => a= ^unaffected^auto cli2 = with_suffix_short_long(":", ":=", option("a", "all"), option("b"), ... ); // => a: 모두:= b:
플래그 그룹을 결합 가능하게 만듭니다.
auto cli1 = 결합 가능( option("-a"), option("-b")); //"-a", "-b", "-ab", "-ba"와 일치합니다. //임의의 공통 접두사와도 작동합니다:auto cli2 = Joinable( option("--xA0"), option("- -xB1")); //"--xA0B1" 또는 "--xB1A0"과도 일치합니다.
명령줄 인터페이스를 코드의 나머지 부분에 연결하는 가장 쉬운 방법은 개체 값이나 함수(개체) 호출을 매개 변수에 바인딩하는 것입니다(여기도 참조).
부울 b = 거짓; int i = 5; int m = 0; 문자열 x; ifstream fs;auto cli = ( option("-b").set(b), // "-b" 감지됨 -> b를 true로 설정option("-m").set(m,2), // " -m" 감지됨 -> m을 2option("-x") & value("X", x)로 설정, // 인수 문자열에서 x의 값 설정 option("-i") & opt_value("i", i) , // i의 값을 설정합니다. arg string option("-v").call( []{ cout << "v"; } ), // 함수 호출(객체) /lambdaoption("-v")( []{ cout << "v "; } ), // 이전 라인과 동일option("-f") & value("file").call([&](string f){ fs.open(f); }) );
프로덕션 코드에서는 아마도 설정 클래스를 사용할 것입니다.
구조체 설정 { bool x = false; /* ... */ }; 설정 cmdline_settings(int argc, char* argv[]) { 설정 s;auto cli = ( option("-x").set(sx), /* ... */ );parse(argc, argv, cli);return s; }
대상은 다음 중 하나여야 합니다.
기본 유형( int, long int, float, double, ...
)
const char*
에서 변환 가능한 유형
호출 가능한 엔터티: 빈 매개변수 목록이 있거나 const char*
에서 변환할 수 있는 정확히 하나의 매개변수가 있는 함수, 함수 개체/람다
그룹 및 매개변수에 대한 독스트링은 멤버 함수 doc
또는 operator %
사용하여 설정할 수 있습니다.
자동 CLI = ( ( option("x").set(x).doc("X 세트"),option("y").set(y) % "Y 세트" ), "문서화된 그룹 1:" % ( option("-g").set(g).doc("G를 활성화합니다"), option("-h").set(h) % "H 활성화" ), ( option("-i").set(i) % "I 활성화", option("-j").set(j) % "J를 활성화합니다" ).doc("문서화된 그룹 2:") );
사용 라인:
cout << Usage_lines(cli, "progname") << 'n';//형식 지정 옵션 포함auto fmt = doc_formatting{} .first_column(3) .last_column(79); cout << Usage_lines(cli, "progname", fmt) << 'n';
자세한 문서:
cout << document(cli) << 'n';//형식 옵션 포함auto fmt = doc_formatting{} .first_column(7) .doc_column(15) .last_column(99); cout << 문서(cli, fmt) << 'n';
매뉴얼 페이지:
auto cli = ( /*명령줄 인터페이스를 정의하는 코드가 여기에 표시됩니다*/ ); cout << make_man_page(cli, "progname") << 'n';//형식 지정 옵션 포함auto fmt = doc_formatting{} .first_column(7) .doc_column(15) .last_column(99); cout << make_man_page(cli, "progname", fmt) << 'n';
각 매개변수에는 이벤트 핸들러 함수가 첨부될 수 있습니다. 이는 매개변수에 매핑된 각 인수에 대해 한 번(또는 누락된 이벤트당 한 번) 호출됩니다.
문자열 파일 = "default.txt";auto param = 필수("-nof").set(file,"") | 필수("-f") & 값("파일", 파일) // 2번째, 3번째, 4번째,... 일치(이 경우 오류가 발생합니다) .if_repeated( [] { /* ... */ } ) // 필요한 값 매개변수가 누락된 경우 .if_missing( [] { /* ... */ } ) // 도달할 수 없는 경우, 예를 들어 파일 이름 앞에 "-f" 플래그가 없습니다. .if_blocked( [] { /* ... */ } ) // 일치 항목이 다른 대체 "-nof"와 충돌하는 경우 .if_contributed( [] { /* ... */ } );
핸들러 함수는 이벤트가 처음 발생한 인수 인덱스로 설정된 int를 취할 수도 있습니다.
문자열 파일 = "default.txt";auto param = 필수("-nof").set(file,"") | 필수("-f") & 값("파일", 파일) .if_repeated ( [] (int argIdx) { /* ... */ } ) .if_missing ( [] (int argIdx) { /* ... */ } ) .if_blocked ( [] (int argIdx) { /* ... */ } ) .if_contributed( [] (int argIdx) { /* ... */ } );
다음 CLI에 대해 -f -b
또는 -b -f -a
명령줄 인수로 지정하면 -f
뒤의 값은 선택 사항이 아니므로 오류가 보고됩니다.
auto cli = ( option("-a"), option("-f") & value("filename"), option("-b") );
이 동작은 대부분의 사용 사례에 적합합니다. 하지만 파일 이름이 플래그 이름과 충돌할 수도 있기 때문에 프로그램이 어떤 문자열이든 파일 이름으로 사용하도록 하려면 어떻게 해야 할까요? operator !
. 이렇게 하면 -f
이후의 다음 문자열은 -f
주어지자마자 항상 가장 높은 우선순위와 일치됩니다.
auto cli = ( option("-a"), option("-f") & !value("filename"), option("-b") );// ^~~~~~
탐욕스러운 매개변수에는 매우 주의 하세요!
auto cli = ( /* 여기에 인터페이스 */ );auto res = pars(argc, argv, cli);if(res.any_error()) { /* ... */ }//aggregated errorif(res.unmapped_args_count ()) { /* ... */ }if(res.any_bad_repeat()) { /* ... */ }if(res.any_blocked()) { /* ... */ }if(res.any_contribute()) { /* ... */ }