├── .gitignore ├── .gitmodules ├── LICENSE.md ├── Makefile ├── README.md ├── cxxopts.hpp └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | sfizz-render 3 | main.o -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "midifile"] 2 | path = midifile 3 | url = https://github.com/craigsapp/midifile 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, Paul Ferrand 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | name = sfizz-render 2 | src = main.cpp 3 | obj = $(src:.cpp=.o) 4 | 5 | LDFLAGS = `pkg-config --libs sndfile sfizz` -Lmidifile/lib -l:libmidifile.a 6 | CXXFLAGS += -Imidifile/include -std=c++17 -O3 7 | 8 | $(name): $(obj) libmidifile 9 | $(CXX) -o $@ $(obj) $(LDFLAGS) 10 | 11 | .PHONY: clean 12 | clean: 13 | rm -f $(obj) $(name) 14 | 15 | .PHONY: libmidifile 16 | libmidifile: 17 | $(MAKE) -C midifile library -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sfizz-render 2 | 3 | **sfizz-render now is bundled in https://github.com/sfztools/sfizz** 4 | 5 | ## Building and installing 6 | 7 | `sfizz-render` requires the `sndfile` library ; on Debian-based systems, you can install `libsndfile1-dev`. 8 | It also requires `sfizz` installed as a shared library. 9 | On Arch you have AUR packages available for this, or install it from source following the information in (https://github.com/sfztools/sfizz). 10 | To build the release from source use 11 | ``` 12 | git clone --recursive https://github.com/sfztools/sfizz-render.git 13 | cd sfizz-render 14 | make 15 | ``` 16 | 17 | ## Usage 18 | 19 | You have to specify 3 elements to `sfizz-render`: 20 | - A MIDI/SMF file 21 | - An SFZ file 22 | - An output WAV file 23 | The output file can only be a stereo WAV file for now, not because of technical reasons because `libsndfile` is very versatile, but because I did not want to multiply the possible flags. 24 | 25 | The basic usage is 26 | ``` 27 | sfizz-render --wav wav_file.wav --sfz sfz_file.sfz --midi midi_file.mid 28 | ``` 29 | 30 | The complete list of command line options is: 31 | ``` 32 | Render a midi file through an SFZ file using the sfizz library. 33 | Usage: 34 | sfizz-render [OPTION...] 35 | 36 | --sfz arg SFZ file 37 | --midi arg Input midi file 38 | --wav arg Output wav file 39 | -b, --blocksize arg Block size for the sfizz callbacks 40 | -s, --samplerate arg Output sample rate 41 | -t, --track arg Track number to use 42 | --oversampling arg Internal oversampling factor 43 | -v, --verbose Verbose output 44 | --use-eot End the rendering at the last End of Track Midi 45 | message 46 | -h, --help Show help 47 | ``` 48 | -------------------------------------------------------------------------------- /cxxopts.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef CXXOPTS_HPP_INCLUDED 26 | #define CXXOPTS_HPP_INCLUDED 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #ifdef __cpp_lib_optional 43 | #include 44 | #define CXXOPTS_HAS_OPTIONAL 45 | #endif 46 | 47 | #ifndef CXXOPTS_VECTOR_DELIMITER 48 | #define CXXOPTS_VECTOR_DELIMITER ',' 49 | #endif 50 | 51 | #define CXXOPTS__VERSION_MAJOR 2 52 | #define CXXOPTS__VERSION_MINOR 2 53 | #define CXXOPTS__VERSION_PATCH 0 54 | 55 | namespace cxxopts 56 | { 57 | static constexpr struct { 58 | uint8_t major, minor, patch; 59 | } version = { 60 | CXXOPTS__VERSION_MAJOR, 61 | CXXOPTS__VERSION_MINOR, 62 | CXXOPTS__VERSION_PATCH 63 | }; 64 | } 65 | 66 | //when we ask cxxopts to use Unicode, help strings are processed using ICU, 67 | //which results in the correct lengths being computed for strings when they 68 | //are formatted for the help output 69 | //it is necessary to make sure that can be found by the 70 | //compiler, and that icu-uc is linked in to the binary. 71 | 72 | #ifdef CXXOPTS_USE_UNICODE 73 | #include 74 | 75 | namespace cxxopts 76 | { 77 | typedef icu::UnicodeString String; 78 | 79 | inline 80 | String 81 | toLocalString(std::string s) 82 | { 83 | return icu::UnicodeString::fromUTF8(std::move(s)); 84 | } 85 | 86 | class UnicodeStringIterator : public 87 | std::iterator 88 | { 89 | public: 90 | 91 | UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) 92 | : s(string) 93 | , i(pos) 94 | { 95 | } 96 | 97 | value_type 98 | operator*() const 99 | { 100 | return s->char32At(i); 101 | } 102 | 103 | bool 104 | operator==(const UnicodeStringIterator& rhs) const 105 | { 106 | return s == rhs.s && i == rhs.i; 107 | } 108 | 109 | bool 110 | operator!=(const UnicodeStringIterator& rhs) const 111 | { 112 | return !(*this == rhs); 113 | } 114 | 115 | UnicodeStringIterator& 116 | operator++() 117 | { 118 | ++i; 119 | return *this; 120 | } 121 | 122 | UnicodeStringIterator 123 | operator+(int32_t v) 124 | { 125 | return UnicodeStringIterator(s, i + v); 126 | } 127 | 128 | private: 129 | const icu::UnicodeString* s; 130 | int32_t i; 131 | }; 132 | 133 | inline 134 | String& 135 | stringAppend(String&s, String a) 136 | { 137 | return s.append(std::move(a)); 138 | } 139 | 140 | inline 141 | String& 142 | stringAppend(String& s, int n, UChar32 c) 143 | { 144 | for (int i = 0; i != n; ++i) 145 | { 146 | s.append(c); 147 | } 148 | 149 | return s; 150 | } 151 | 152 | template 153 | String& 154 | stringAppend(String& s, Iterator begin, Iterator end) 155 | { 156 | while (begin != end) 157 | { 158 | s.append(*begin); 159 | ++begin; 160 | } 161 | 162 | return s; 163 | } 164 | 165 | inline 166 | size_t 167 | stringLength(const String& s) 168 | { 169 | return s.length(); 170 | } 171 | 172 | inline 173 | std::string 174 | toUTF8String(const String& s) 175 | { 176 | std::string result; 177 | s.toUTF8String(result); 178 | 179 | return result; 180 | } 181 | 182 | inline 183 | bool 184 | empty(const String& s) 185 | { 186 | return s.isEmpty(); 187 | } 188 | } 189 | 190 | namespace std 191 | { 192 | inline 193 | cxxopts::UnicodeStringIterator 194 | begin(const icu::UnicodeString& s) 195 | { 196 | return cxxopts::UnicodeStringIterator(&s, 0); 197 | } 198 | 199 | inline 200 | cxxopts::UnicodeStringIterator 201 | end(const icu::UnicodeString& s) 202 | { 203 | return cxxopts::UnicodeStringIterator(&s, s.length()); 204 | } 205 | } 206 | 207 | //ifdef CXXOPTS_USE_UNICODE 208 | #else 209 | 210 | namespace cxxopts 211 | { 212 | typedef std::string String; 213 | 214 | template 215 | T 216 | toLocalString(T&& t) 217 | { 218 | return std::forward(t); 219 | } 220 | 221 | inline 222 | size_t 223 | stringLength(const String& s) 224 | { 225 | return s.length(); 226 | } 227 | 228 | inline 229 | String& 230 | stringAppend(String&s, String a) 231 | { 232 | return s.append(std::move(a)); 233 | } 234 | 235 | inline 236 | String& 237 | stringAppend(String& s, size_t n, char c) 238 | { 239 | return s.append(n, c); 240 | } 241 | 242 | template 243 | String& 244 | stringAppend(String& s, Iterator begin, Iterator end) 245 | { 246 | return s.append(begin, end); 247 | } 248 | 249 | template 250 | std::string 251 | toUTF8String(T&& t) 252 | { 253 | return std::forward(t); 254 | } 255 | 256 | inline 257 | bool 258 | empty(const std::string& s) 259 | { 260 | return s.empty(); 261 | } 262 | } 263 | 264 | //ifdef CXXOPTS_USE_UNICODE 265 | #endif 266 | 267 | namespace cxxopts 268 | { 269 | namespace 270 | { 271 | #ifdef _WIN32 272 | const std::string LQUOTE("\'"); 273 | const std::string RQUOTE("\'"); 274 | #else 275 | const std::string LQUOTE("‘"); 276 | const std::string RQUOTE("’"); 277 | #endif 278 | } 279 | 280 | class Value : public std::enable_shared_from_this 281 | { 282 | public: 283 | 284 | virtual ~Value() = default; 285 | 286 | virtual 287 | std::shared_ptr 288 | clone() const = 0; 289 | 290 | virtual void 291 | parse(const std::string& text) const = 0; 292 | 293 | virtual void 294 | parse() const = 0; 295 | 296 | virtual bool 297 | has_default() const = 0; 298 | 299 | virtual bool 300 | is_container() const = 0; 301 | 302 | virtual bool 303 | has_implicit() const = 0; 304 | 305 | virtual std::string 306 | get_default_value() const = 0; 307 | 308 | virtual std::string 309 | get_implicit_value() const = 0; 310 | 311 | virtual std::shared_ptr 312 | default_value(const std::string& value) = 0; 313 | 314 | virtual std::shared_ptr 315 | implicit_value(const std::string& value) = 0; 316 | 317 | virtual std::shared_ptr 318 | no_implicit_value() = 0; 319 | 320 | virtual bool 321 | is_boolean() const = 0; 322 | }; 323 | 324 | class OptionException : public std::exception 325 | { 326 | public: 327 | OptionException(const std::string& message) 328 | : m_message(message) 329 | { 330 | } 331 | 332 | virtual const char* 333 | what() const noexcept 334 | { 335 | return m_message.c_str(); 336 | } 337 | 338 | private: 339 | std::string m_message; 340 | }; 341 | 342 | class OptionSpecException : public OptionException 343 | { 344 | public: 345 | 346 | OptionSpecException(const std::string& message) 347 | : OptionException(message) 348 | { 349 | } 350 | }; 351 | 352 | class OptionParseException : public OptionException 353 | { 354 | public: 355 | OptionParseException(const std::string& message) 356 | : OptionException(message) 357 | { 358 | } 359 | }; 360 | 361 | class option_exists_error : public OptionSpecException 362 | { 363 | public: 364 | option_exists_error(const std::string& option) 365 | : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") 366 | { 367 | } 368 | }; 369 | 370 | class invalid_option_format_error : public OptionSpecException 371 | { 372 | public: 373 | invalid_option_format_error(const std::string& format) 374 | : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) 375 | { 376 | } 377 | }; 378 | 379 | class option_syntax_exception : public OptionParseException { 380 | public: 381 | option_syntax_exception(const std::string& text) 382 | : OptionParseException("Argument " + LQUOTE + text + RQUOTE + 383 | " starts with a - but has incorrect syntax") 384 | { 385 | } 386 | }; 387 | 388 | class option_not_exists_exception : public OptionParseException 389 | { 390 | public: 391 | option_not_exists_exception(const std::string& option) 392 | : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") 393 | { 394 | } 395 | }; 396 | 397 | class missing_argument_exception : public OptionParseException 398 | { 399 | public: 400 | missing_argument_exception(const std::string& option) 401 | : OptionParseException( 402 | "Option " + LQUOTE + option + RQUOTE + " is missing an argument" 403 | ) 404 | { 405 | } 406 | }; 407 | 408 | class option_requires_argument_exception : public OptionParseException 409 | { 410 | public: 411 | option_requires_argument_exception(const std::string& option) 412 | : OptionParseException( 413 | "Option " + LQUOTE + option + RQUOTE + " requires an argument" 414 | ) 415 | { 416 | } 417 | }; 418 | 419 | class option_not_has_argument_exception : public OptionParseException 420 | { 421 | public: 422 | option_not_has_argument_exception 423 | ( 424 | const std::string& option, 425 | const std::string& arg 426 | ) 427 | : OptionParseException( 428 | "Option " + LQUOTE + option + RQUOTE + 429 | " does not take an argument, but argument " + 430 | LQUOTE + arg + RQUOTE + " given" 431 | ) 432 | { 433 | } 434 | }; 435 | 436 | class option_not_present_exception : public OptionParseException 437 | { 438 | public: 439 | option_not_present_exception(const std::string& option) 440 | : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") 441 | { 442 | } 443 | }; 444 | 445 | class argument_incorrect_type : public OptionParseException 446 | { 447 | public: 448 | argument_incorrect_type 449 | ( 450 | const std::string& arg 451 | ) 452 | : OptionParseException( 453 | "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" 454 | ) 455 | { 456 | } 457 | }; 458 | 459 | class option_required_exception : public OptionParseException 460 | { 461 | public: 462 | option_required_exception(const std::string& option) 463 | : OptionParseException( 464 | "Option " + LQUOTE + option + RQUOTE + " is required but not present" 465 | ) 466 | { 467 | } 468 | }; 469 | 470 | template 471 | void throw_or_mimic(const std::string& text) 472 | { 473 | static_assert(std::is_base_of::value, 474 | "throw_or_mimic only works on std::exception and " 475 | "deriving classes"); 476 | 477 | #ifndef CXXOPTS_NO_EXCEPTIONS 478 | // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw 479 | throw T{text}; 480 | #else 481 | // Otherwise manually instantiate the exception, print what() to stderr, 482 | // and abort 483 | T exception{text}; 484 | std::cerr << exception.what() << std::endl; 485 | std::cerr << "Aborting (exceptions disabled)..." << std::endl; 486 | std::abort(); 487 | #endif 488 | } 489 | 490 | namespace values 491 | { 492 | namespace 493 | { 494 | std::basic_regex integer_pattern 495 | ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); 496 | std::basic_regex truthy_pattern 497 | ("(t|T)(rue)?|1"); 498 | std::basic_regex falsy_pattern 499 | ("(f|F)(alse)?|0"); 500 | } 501 | 502 | namespace detail 503 | { 504 | template 505 | struct SignedCheck; 506 | 507 | template 508 | struct SignedCheck 509 | { 510 | template 511 | void 512 | operator()(bool negative, U u, const std::string& text) 513 | { 514 | if (negative) 515 | { 516 | if (u > static_cast((std::numeric_limits::min)())) 517 | { 518 | throw_or_mimic(text); 519 | } 520 | } 521 | else 522 | { 523 | if (u > static_cast((std::numeric_limits::max)())) 524 | { 525 | throw_or_mimic(text); 526 | } 527 | } 528 | } 529 | }; 530 | 531 | template 532 | struct SignedCheck 533 | { 534 | template 535 | void 536 | operator()(bool, U, const std::string&) {} 537 | }; 538 | 539 | template 540 | void 541 | check_signed_range(bool negative, U value, const std::string& text) 542 | { 543 | SignedCheck::is_signed>()(negative, value, text); 544 | } 545 | } 546 | 547 | template 548 | R 549 | checked_negate(T&& t, const std::string&, std::true_type) 550 | { 551 | // if we got to here, then `t` is a positive number that fits into 552 | // `R`. So to avoid MSVC C4146, we first cast it to `R`. 553 | // See https://github.com/jarro2783/cxxopts/issues/62 for more details. 554 | return static_cast(-static_cast(t-1)-1); 555 | } 556 | 557 | template 558 | T 559 | checked_negate(T&& t, const std::string& text, std::false_type) 560 | { 561 | throw_or_mimic(text); 562 | return t; 563 | } 564 | 565 | template 566 | void 567 | integer_parser(const std::string& text, T& value) 568 | { 569 | std::smatch match; 570 | std::regex_match(text, match, integer_pattern); 571 | 572 | if (match.length() == 0) 573 | { 574 | throw_or_mimic(text); 575 | } 576 | 577 | if (match.length(4) > 0) 578 | { 579 | value = 0; 580 | return; 581 | } 582 | 583 | using US = typename std::make_unsigned::type; 584 | 585 | constexpr bool is_signed = std::numeric_limits::is_signed; 586 | const bool negative = match.length(1) > 0; 587 | const uint8_t base = match.length(2) > 0 ? 16 : 10; 588 | 589 | auto value_match = match[3]; 590 | 591 | US result = 0; 592 | 593 | for (auto iter = value_match.first; iter != value_match.second; ++iter) 594 | { 595 | US digit = 0; 596 | 597 | if (*iter >= '0' && *iter <= '9') 598 | { 599 | digit = static_cast(*iter - '0'); 600 | } 601 | else if (base == 16 && *iter >= 'a' && *iter <= 'f') 602 | { 603 | digit = static_cast(*iter - 'a' + 10); 604 | } 605 | else if (base == 16 && *iter >= 'A' && *iter <= 'F') 606 | { 607 | digit = static_cast(*iter - 'A' + 10); 608 | } 609 | else 610 | { 611 | throw_or_mimic(text); 612 | } 613 | 614 | const US next = static_cast(result * base + digit); 615 | if (result > next) 616 | { 617 | throw_or_mimic(text); 618 | } 619 | 620 | result = next; 621 | } 622 | 623 | detail::check_signed_range(negative, result, text); 624 | 625 | if (negative) 626 | { 627 | value = checked_negate(result, 628 | text, 629 | std::integral_constant()); 630 | } 631 | else 632 | { 633 | value = static_cast(result); 634 | } 635 | } 636 | 637 | template 638 | void stringstream_parser(const std::string& text, T& value) 639 | { 640 | std::stringstream in(text); 641 | in >> value; 642 | if (!in) { 643 | throw_or_mimic(text); 644 | } 645 | } 646 | 647 | inline 648 | void 649 | parse_value(const std::string& text, uint8_t& value) 650 | { 651 | integer_parser(text, value); 652 | } 653 | 654 | inline 655 | void 656 | parse_value(const std::string& text, int8_t& value) 657 | { 658 | integer_parser(text, value); 659 | } 660 | 661 | inline 662 | void 663 | parse_value(const std::string& text, uint16_t& value) 664 | { 665 | integer_parser(text, value); 666 | } 667 | 668 | inline 669 | void 670 | parse_value(const std::string& text, int16_t& value) 671 | { 672 | integer_parser(text, value); 673 | } 674 | 675 | inline 676 | void 677 | parse_value(const std::string& text, uint32_t& value) 678 | { 679 | integer_parser(text, value); 680 | } 681 | 682 | inline 683 | void 684 | parse_value(const std::string& text, int32_t& value) 685 | { 686 | integer_parser(text, value); 687 | } 688 | 689 | inline 690 | void 691 | parse_value(const std::string& text, uint64_t& value) 692 | { 693 | integer_parser(text, value); 694 | } 695 | 696 | inline 697 | void 698 | parse_value(const std::string& text, int64_t& value) 699 | { 700 | integer_parser(text, value); 701 | } 702 | 703 | inline 704 | void 705 | parse_value(const std::string& text, bool& value) 706 | { 707 | std::smatch result; 708 | std::regex_match(text, result, truthy_pattern); 709 | 710 | if (!result.empty()) 711 | { 712 | value = true; 713 | return; 714 | } 715 | 716 | std::regex_match(text, result, falsy_pattern); 717 | if (!result.empty()) 718 | { 719 | value = false; 720 | return; 721 | } 722 | 723 | throw_or_mimic(text); 724 | } 725 | 726 | inline 727 | void 728 | parse_value(const std::string& text, std::string& value) 729 | { 730 | value = text; 731 | } 732 | 733 | // The fallback parser. It uses the stringstream parser to parse all types 734 | // that have not been overloaded explicitly. It has to be placed in the 735 | // source code before all other more specialized templates. 736 | template 737 | void 738 | parse_value(const std::string& text, T& value) { 739 | stringstream_parser(text, value); 740 | } 741 | 742 | template 743 | void 744 | parse_value(const std::string& text, std::vector& value) 745 | { 746 | std::stringstream in(text); 747 | std::string token; 748 | while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { 749 | T v; 750 | parse_value(token, v); 751 | value.emplace_back(std::move(v)); 752 | } 753 | } 754 | 755 | #ifdef CXXOPTS_HAS_OPTIONAL 756 | template 757 | void 758 | parse_value(const std::string& text, std::optional& value) 759 | { 760 | T result; 761 | parse_value(text, result); 762 | value = std::move(result); 763 | } 764 | #endif 765 | 766 | inline 767 | void parse_value(const std::string& text, char& c) 768 | { 769 | if (text.length() != 1) 770 | { 771 | throw_or_mimic(text); 772 | } 773 | 774 | c = text[0]; 775 | } 776 | 777 | template 778 | struct type_is_container 779 | { 780 | static constexpr bool value = false; 781 | }; 782 | 783 | template 784 | struct type_is_container> 785 | { 786 | static constexpr bool value = true; 787 | }; 788 | 789 | template 790 | class abstract_value : public Value 791 | { 792 | using Self = abstract_value; 793 | 794 | public: 795 | abstract_value() 796 | : m_result(std::make_shared()) 797 | , m_store(m_result.get()) 798 | { 799 | } 800 | 801 | abstract_value(T* t) 802 | : m_store(t) 803 | { 804 | } 805 | 806 | virtual ~abstract_value() = default; 807 | 808 | abstract_value(const abstract_value& rhs) 809 | { 810 | if (rhs.m_result) 811 | { 812 | m_result = std::make_shared(); 813 | m_store = m_result.get(); 814 | } 815 | else 816 | { 817 | m_store = rhs.m_store; 818 | } 819 | 820 | m_default = rhs.m_default; 821 | m_implicit = rhs.m_implicit; 822 | m_default_value = rhs.m_default_value; 823 | m_implicit_value = rhs.m_implicit_value; 824 | } 825 | 826 | void 827 | parse(const std::string& text) const 828 | { 829 | parse_value(text, *m_store); 830 | } 831 | 832 | bool 833 | is_container() const 834 | { 835 | return type_is_container::value; 836 | } 837 | 838 | void 839 | parse() const 840 | { 841 | parse_value(m_default_value, *m_store); 842 | } 843 | 844 | bool 845 | has_default() const 846 | { 847 | return m_default; 848 | } 849 | 850 | bool 851 | has_implicit() const 852 | { 853 | return m_implicit; 854 | } 855 | 856 | std::shared_ptr 857 | default_value(const std::string& value) 858 | { 859 | m_default = true; 860 | m_default_value = value; 861 | return shared_from_this(); 862 | } 863 | 864 | std::shared_ptr 865 | implicit_value(const std::string& value) 866 | { 867 | m_implicit = true; 868 | m_implicit_value = value; 869 | return shared_from_this(); 870 | } 871 | 872 | std::shared_ptr 873 | no_implicit_value() 874 | { 875 | m_implicit = false; 876 | return shared_from_this(); 877 | } 878 | 879 | std::string 880 | get_default_value() const 881 | { 882 | return m_default_value; 883 | } 884 | 885 | std::string 886 | get_implicit_value() const 887 | { 888 | return m_implicit_value; 889 | } 890 | 891 | bool 892 | is_boolean() const 893 | { 894 | return std::is_same::value; 895 | } 896 | 897 | const T& 898 | get() const 899 | { 900 | if (m_store == nullptr) 901 | { 902 | return *m_result; 903 | } 904 | else 905 | { 906 | return *m_store; 907 | } 908 | } 909 | 910 | protected: 911 | std::shared_ptr m_result; 912 | T* m_store; 913 | 914 | bool m_default = false; 915 | bool m_implicit = false; 916 | 917 | std::string m_default_value; 918 | std::string m_implicit_value; 919 | }; 920 | 921 | template 922 | class standard_value : public abstract_value 923 | { 924 | public: 925 | using abstract_value::abstract_value; 926 | 927 | std::shared_ptr 928 | clone() const 929 | { 930 | return std::make_shared>(*this); 931 | } 932 | }; 933 | 934 | template <> 935 | class standard_value : public abstract_value 936 | { 937 | public: 938 | ~standard_value() = default; 939 | 940 | standard_value() 941 | { 942 | set_default_and_implicit(); 943 | } 944 | 945 | standard_value(bool* b) 946 | : abstract_value(b) 947 | { 948 | set_default_and_implicit(); 949 | } 950 | 951 | std::shared_ptr 952 | clone() const 953 | { 954 | return std::make_shared>(*this); 955 | } 956 | 957 | private: 958 | 959 | void 960 | set_default_and_implicit() 961 | { 962 | m_default = true; 963 | m_default_value = "false"; 964 | m_implicit = true; 965 | m_implicit_value = "true"; 966 | } 967 | }; 968 | } 969 | 970 | template 971 | std::shared_ptr 972 | value() 973 | { 974 | return std::make_shared>(); 975 | } 976 | 977 | template 978 | std::shared_ptr 979 | value(T& t) 980 | { 981 | return std::make_shared>(&t); 982 | } 983 | 984 | class OptionAdder; 985 | 986 | class OptionDetails 987 | { 988 | public: 989 | OptionDetails 990 | ( 991 | const std::string& short_, 992 | const std::string& long_, 993 | const String& desc, 994 | std::shared_ptr val 995 | ) 996 | : m_short(short_) 997 | , m_long(long_) 998 | , m_desc(desc) 999 | , m_value(val) 1000 | , m_count(0) 1001 | { 1002 | } 1003 | 1004 | OptionDetails(const OptionDetails& rhs) 1005 | : m_desc(rhs.m_desc) 1006 | , m_count(rhs.m_count) 1007 | { 1008 | m_value = rhs.m_value->clone(); 1009 | } 1010 | 1011 | OptionDetails(OptionDetails&& rhs) = default; 1012 | 1013 | const String& 1014 | description() const 1015 | { 1016 | return m_desc; 1017 | } 1018 | 1019 | const Value& value() const { 1020 | return *m_value; 1021 | } 1022 | 1023 | std::shared_ptr 1024 | make_storage() const 1025 | { 1026 | return m_value->clone(); 1027 | } 1028 | 1029 | const std::string& 1030 | short_name() const 1031 | { 1032 | return m_short; 1033 | } 1034 | 1035 | const std::string& 1036 | long_name() const 1037 | { 1038 | return m_long; 1039 | } 1040 | 1041 | private: 1042 | std::string m_short; 1043 | std::string m_long; 1044 | String m_desc; 1045 | std::shared_ptr m_value; 1046 | int m_count; 1047 | }; 1048 | 1049 | struct HelpOptionDetails 1050 | { 1051 | std::string s; 1052 | std::string l; 1053 | String desc; 1054 | bool has_default; 1055 | std::string default_value; 1056 | bool has_implicit; 1057 | std::string implicit_value; 1058 | std::string arg_help; 1059 | bool is_container; 1060 | bool is_boolean; 1061 | }; 1062 | 1063 | struct HelpGroupDetails 1064 | { 1065 | std::string name; 1066 | std::string description; 1067 | std::vector options; 1068 | }; 1069 | 1070 | class OptionValue 1071 | { 1072 | public: 1073 | void 1074 | parse 1075 | ( 1076 | std::shared_ptr details, 1077 | const std::string& text 1078 | ) 1079 | { 1080 | ensure_value(details); 1081 | ++m_count; 1082 | m_value->parse(text); 1083 | } 1084 | 1085 | void 1086 | parse_default(std::shared_ptr details) 1087 | { 1088 | ensure_value(details); 1089 | m_default = true; 1090 | m_value->parse(); 1091 | } 1092 | 1093 | size_t 1094 | count() const noexcept 1095 | { 1096 | return m_count; 1097 | } 1098 | 1099 | // TODO: maybe default options should count towards the number of arguments 1100 | bool 1101 | has_default() const noexcept 1102 | { 1103 | return m_default; 1104 | } 1105 | 1106 | template 1107 | const T& 1108 | as() const 1109 | { 1110 | if (m_value == nullptr) { 1111 | throw_or_mimic("No value"); 1112 | } 1113 | 1114 | #ifdef CXXOPTS_NO_RTTI 1115 | return static_cast&>(*m_value).get(); 1116 | #else 1117 | return dynamic_cast&>(*m_value).get(); 1118 | #endif 1119 | } 1120 | 1121 | private: 1122 | void 1123 | ensure_value(std::shared_ptr details) 1124 | { 1125 | if (m_value == nullptr) 1126 | { 1127 | m_value = details->make_storage(); 1128 | } 1129 | } 1130 | 1131 | std::shared_ptr m_value; 1132 | size_t m_count = 0; 1133 | bool m_default = false; 1134 | }; 1135 | 1136 | class KeyValue 1137 | { 1138 | public: 1139 | KeyValue(std::string key_, std::string value_) 1140 | : m_key(std::move(key_)) 1141 | , m_value(std::move(value_)) 1142 | { 1143 | } 1144 | 1145 | const 1146 | std::string& 1147 | key() const 1148 | { 1149 | return m_key; 1150 | } 1151 | 1152 | const 1153 | std::string& 1154 | value() const 1155 | { 1156 | return m_value; 1157 | } 1158 | 1159 | template 1160 | T 1161 | as() const 1162 | { 1163 | T result; 1164 | values::parse_value(m_value, result); 1165 | return result; 1166 | } 1167 | 1168 | private: 1169 | std::string m_key; 1170 | std::string m_value; 1171 | }; 1172 | 1173 | class ParseResult 1174 | { 1175 | public: 1176 | 1177 | ParseResult( 1178 | const std::shared_ptr< 1179 | std::unordered_map> 1180 | >, 1181 | std::vector, 1182 | bool allow_unrecognised, 1183 | int&, char**&); 1184 | 1185 | size_t 1186 | count(const std::string& o) const 1187 | { 1188 | auto iter = m_options->find(o); 1189 | if (iter == m_options->end()) 1190 | { 1191 | return 0; 1192 | } 1193 | 1194 | auto riter = m_results.find(iter->second); 1195 | 1196 | return riter->second.count(); 1197 | } 1198 | 1199 | const OptionValue& 1200 | operator[](const std::string& option) const 1201 | { 1202 | auto iter = m_options->find(option); 1203 | 1204 | if (iter == m_options->end()) 1205 | { 1206 | throw_or_mimic(option); 1207 | } 1208 | 1209 | auto riter = m_results.find(iter->second); 1210 | 1211 | return riter->second; 1212 | } 1213 | 1214 | const std::vector& 1215 | arguments() const 1216 | { 1217 | return m_sequential; 1218 | } 1219 | 1220 | private: 1221 | 1222 | void 1223 | parse(int& argc, char**& argv); 1224 | 1225 | void 1226 | add_to_option(const std::string& option, const std::string& arg); 1227 | 1228 | bool 1229 | consume_positional(std::string a); 1230 | 1231 | void 1232 | parse_option 1233 | ( 1234 | std::shared_ptr value, 1235 | const std::string& name, 1236 | const std::string& arg = "" 1237 | ); 1238 | 1239 | void 1240 | parse_default(std::shared_ptr details); 1241 | 1242 | void 1243 | checked_parse_arg 1244 | ( 1245 | int argc, 1246 | char* argv[], 1247 | int& current, 1248 | std::shared_ptr value, 1249 | const std::string& name 1250 | ); 1251 | 1252 | const std::shared_ptr< 1253 | std::unordered_map> 1254 | > m_options; 1255 | std::vector m_positional; 1256 | std::vector::iterator m_next_positional; 1257 | std::unordered_set m_positional_set; 1258 | std::unordered_map, OptionValue> m_results; 1259 | 1260 | bool m_allow_unrecognised; 1261 | 1262 | std::vector m_sequential; 1263 | }; 1264 | 1265 | struct Option 1266 | { 1267 | Option 1268 | ( 1269 | const std::string& opts, 1270 | const std::string& desc, 1271 | const std::shared_ptr& value = ::cxxopts::value(), 1272 | const std::string& arg_help = "" 1273 | ) 1274 | : opts_(opts) 1275 | , desc_(desc) 1276 | , value_(value) 1277 | , arg_help_(arg_help) 1278 | { 1279 | } 1280 | 1281 | std::string opts_; 1282 | std::string desc_; 1283 | std::shared_ptr value_; 1284 | std::string arg_help_; 1285 | }; 1286 | 1287 | class Options 1288 | { 1289 | typedef std::unordered_map> 1290 | OptionMap; 1291 | public: 1292 | 1293 | Options(std::string program, std::string help_string = "") 1294 | : m_program(std::move(program)) 1295 | , m_help_string(toLocalString(std::move(help_string))) 1296 | , m_custom_help("[OPTION...]") 1297 | , m_positional_help("positional parameters") 1298 | , m_show_positional(false) 1299 | , m_allow_unrecognised(false) 1300 | , m_options(std::make_shared()) 1301 | , m_next_positional(m_positional.end()) 1302 | { 1303 | } 1304 | 1305 | Options& 1306 | positional_help(std::string help_text) 1307 | { 1308 | m_positional_help = std::move(help_text); 1309 | return *this; 1310 | } 1311 | 1312 | Options& 1313 | custom_help(std::string help_text) 1314 | { 1315 | m_custom_help = std::move(help_text); 1316 | return *this; 1317 | } 1318 | 1319 | Options& 1320 | show_positional_help() 1321 | { 1322 | m_show_positional = true; 1323 | return *this; 1324 | } 1325 | 1326 | Options& 1327 | allow_unrecognised_options() 1328 | { 1329 | m_allow_unrecognised = true; 1330 | return *this; 1331 | } 1332 | 1333 | ParseResult 1334 | parse(int& argc, char**& argv); 1335 | 1336 | OptionAdder 1337 | add_options(std::string group = ""); 1338 | 1339 | void 1340 | add_options 1341 | ( 1342 | const std::string& group, 1343 | std::initializer_list