Skip to content

Commit 57dd5fe

Browse files
author
Fytch
committed
[BREAKING] replace PROGRAMOPTIONS_SILENT with .silent()
1 parent 8d409fb commit 57dd5fe

13 files changed

+103
-74
lines changed

ProgramOptions.hxx

Lines changed: 84 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -168,23 +168,6 @@ namespace po {
168168
}
169169
#endif // !PROGRAMOPTIONS_NO_COLORS
170170

171-
#ifdef PROGRAMOPTIONS_SILENT
172-
inline std::ostream& err() {
173-
struct dummy_buf_t : public std::streambuf {
174-
int overflow( int c ) {
175-
return c;
176-
}
177-
};
178-
static dummy_buf_t dummy_buf;
179-
static std::ostream dummy_str{ &dummy_buf };
180-
return dummy_str;
181-
}
182-
#else // PROGRAMOPTIONS_SILENT
183-
inline std::ostream& err() {
184-
return std::cerr;
185-
}
186-
#endif // PROGRAMOPTIONS_SILENT
187-
188171
struct error_t {
189172
friend std::ostream& operator<<( std::ostream& stream, error_t const& /* object */ ) { // -Wunused-parameter
190173
return stream << red << "error: ";
@@ -224,15 +207,6 @@ namespace po {
224207
return { arg };
225208
}
226209

227-
template< typename arg_t >
228-
void error_nonoption_arguments( arg_t const& arg ) {
229-
err() << error() << "non-option arguments not allowed" << ignoring( arg );
230-
}
231-
template< typename arg_t >
232-
void error_unrecognized_option( arg_t const& arg ) {
233-
err() << error() << "unrecognized option" << ignoring( arg );
234-
}
235-
236210
// Compatibility stuff for the lack of C++14 support
237211
template< typename T, typename... args_t >
238212
std::unique_ptr< T > make_unique( args_t&&... args ) {
@@ -1654,6 +1628,37 @@ namespace po {
16541628
using options_t = std::unordered_map< std::string, option >;
16551629
options_t m_options;
16561630
char const* m_program_name = "";
1631+
std::ostream* m_output_destination;
1632+
1633+
public:
1634+
parser() {
1635+
verbose( std::cerr );
1636+
}
1637+
1638+
void silent() {
1639+
m_output_destination = nullptr;
1640+
}
1641+
bool is_silent() const {
1642+
return m_output_destination == nullptr;
1643+
}
1644+
void verbose( std::ostream& destination ) {
1645+
m_output_destination = &destination;
1646+
}
1647+
bool is_verbose() const {
1648+
return !is_silent();
1649+
}
1650+
1651+
private:
1652+
template< typename arg_t >
1653+
void error_nonoption_arguments( arg_t const& arg ) {
1654+
assert( is_verbose() );
1655+
*m_output_destination << error() << "non-option arguments not allowed" << ignoring( arg );
1656+
}
1657+
template< typename arg_t >
1658+
void error_unrecognized_option( arg_t const& arg ) {
1659+
assert( is_verbose() );
1660+
*m_output_destination << error() << "unrecognized option" << ignoring( arg );
1661+
}
16571662

16581663
options_t::iterator find_abbreviation( char value ) {
16591664
auto iter = m_options.begin();
@@ -1664,16 +1669,18 @@ namespace po {
16641669
}
16651670

16661671
void check_spelling( char short_option ) {
1672+
assert( is_verbose() );
16671673
if( !std::isalpha( short_option ) )
16681674
return;
16691675
if( std::islower( short_option ) )
16701676
short_option = std::toupper( short_option );
16711677
else // if( std::isupper( short_option ) )
16721678
short_option = std::tolower( short_option );
16731679
if( find_abbreviation( short_option ) != m_options.end() )
1674-
err() << suggest( std::string{ '-', short_option } );
1680+
*m_output_destination << suggest( std::string{ '-', short_option } );
16751681
}
16761682
void check_spelling( char const* long_option ) {
1683+
assert( is_verbose() );
16771684
assert( long_option[ 0 ] == '-' && long_option[ 1 ] == '-' && long_option[ 2 ] != '\0' );
16781685
enum : std::size_t {
16791686
distance_cutoff = 4
@@ -1698,31 +1705,32 @@ namespace po {
16981705
}
16991706
}
17001707
if( min_distance < distance_cutoff )
1701-
err() << suggest( std::string( "--" ) + nearest_option->first );
1708+
*m_output_destination << suggest( std::string( "--" ) + nearest_option->first );
17021709
}
17031710
template< typename expression_t, typename... args_t >
17041711
bool parse_argument( options_t::iterator option, expression_t const& expression, args_t&&... args ) const {
17051712
const error_code code = option->second.parse( std::forward< args_t >( args )... );
17061713
if( code == error_code::none )
17071714
return true;
1708-
err() << error() << "option \'";
1709-
err() << blue << option->first;
1710-
err() << "\' ";
1711-
switch( code ) {
1712-
case error_code::argument_expected:
1713-
case error_code::conversion_error:
1714-
err() << "expects an argument of type " << vt2str( option->second.get_type() );
1715-
break;
1716-
case error_code::no_argument_expected:
1717-
err() << "doesn't expect any arguments";
1718-
break;
1719-
case error_code::out_of_range:
1720-
err() << "has an argument that caused an out of range error";
1721-
break;
1722-
case error_code::none:
1723-
;
1715+
if( is_verbose() ) {
1716+
*m_output_destination << error() << "option \'";
1717+
*m_output_destination << blue << option->first;
1718+
*m_output_destination << "\' ";
1719+
switch( code ) {
1720+
case error_code::argument_expected:
1721+
case error_code::conversion_error:
1722+
*m_output_destination << "expects an argument of type " << vt2str( option->second.get_type() );
1723+
break;
1724+
case error_code::no_argument_expected:
1725+
*m_output_destination << "doesn't expect any arguments";
1726+
break;
1727+
case error_code::out_of_range:
1728+
*m_output_destination << "has an argument that caused an out of range error";
1729+
default: // -Wswitch
1730+
;
1731+
}
1732+
*m_output_destination << ignoring( expression ) << '\n';
17241733
}
1725-
err() << ignoring( expression ) << '\n';
17261734
return false;
17271735
}
17281736
bool dashed_non_option( char* arg ) {
@@ -1752,7 +1760,12 @@ namespace po {
17521760
// -vdata
17531761
argument = &argv[ i ][ j ];
17541762
} else {
1755-
err() << error() << "unexpected character \'" << argv[ i ][ j ] << "\'" << ignoring( argv[ i ] ) << suggest( std::string{ &argv[ i ][ 0 ], &argv[ i ][ j ] } + "=" + std::string{ &argv[ i ][ j ] } ) << '\n';
1763+
if( is_verbose() )
1764+
*m_output_destination
1765+
<< error()
1766+
<< "unexpected character \'" << argv[ i ][ j ] << "\'"
1767+
<< ignoring( argv[ i ] )
1768+
<< suggest( std::string{ &argv[ i ][ 0 ], &argv[ i ][ j ] } + "=" + std::string{ &argv[ i ][ j ] } ) << '\n';
17561769
return false;
17571770
}
17581771
return parse_argument( option, std::move( expression ), argument );
@@ -1762,7 +1775,8 @@ namespace po {
17621775
if( !result ) {
17631776
good = false;
17641777
error_nonoption_arguments( arg );
1765-
err() << '\n';
1778+
if( is_verbose() )
1779+
*m_output_destination << '\n';
17661780
}
17671781
return result;
17681782
}
@@ -1833,15 +1847,19 @@ namespace po {
18331847
for( ; valid_designator_character( *last ); ++last );
18341848
if( first == last ) {
18351849
good = false;
1836-
error_unrecognized_option( argv[ i ] );
1837-
err() << '\n';
1850+
if( is_verbose() ) {
1851+
error_unrecognized_option( argv[ i ] );
1852+
*m_output_destination << '\n';
1853+
}
18381854
} else {
18391855
const auto opt = m_options.find( std::string{ first, last } );
18401856
if( opt == m_options.end() ) {
18411857
good = false;
1842-
error_unrecognized_option( argv[ i ] );
1843-
check_spelling( argv[ i ] );
1844-
err() << '\n';
1858+
if( is_verbose() ) {
1859+
error_unrecognized_option( argv[ i ] );
1860+
check_spelling( argv[ i ] );
1861+
*m_output_destination << '\n';
1862+
}
18451863
} else {
18461864
good &= extract_argument( opt, argc, argv, i, last - argv[ i ] );
18471865
}
@@ -1852,29 +1870,35 @@ namespace po {
18521870
const auto head = find_abbreviation( argv[ i ][ 1 ] );
18531871
if( head == m_options.end() ) {
18541872
good = false;
1855-
error_unrecognized_option( argv[ i ] );
1856-
check_spelling( argv[ i ][ 1 ] );
1857-
err() << '\n';
1873+
if( is_verbose() ) {
1874+
error_unrecognized_option( argv[ i ] );
1875+
check_spelling( argv[ i ][ 1 ] );
1876+
*m_output_destination << '\n';
1877+
}
18581878
} else if( head->second.get_type() == void_ ) {
18591879
// -fgh
18601880
char c;
18611881
for( std::size_t j = 1; ( c = argv[ i ][ j ] ) != '\0'; ++j ) {
18621882
if( !std::isprint( c ) || c == '-' ) {
18631883
good = false;
1864-
err() << error() << "invalid character \'" << c << "\'" << ignoring( &argv[ i ][ j ] ) << '\n';
1884+
if( is_verbose() )
1885+
*m_output_destination << error() << "invalid character \'" << c << "\'" << ignoring( &argv[ i ][ j ] ) << '\n';
18651886
break;
18661887
}
18671888
const auto opt = find_abbreviation( c );
18681889
if( opt == m_options.end() ) {
18691890
good = false;
1870-
error_unrecognized_option( c );
1871-
check_spelling( c );
1872-
err() << '\n';
1891+
if( is_verbose() ) {
1892+
error_unrecognized_option( c );
1893+
check_spelling( c );
1894+
*m_output_destination << '\n';
1895+
}
18731896
continue;
18741897
}
18751898
if( opt->second.get_type() != void_ ) {
18761899
good = false;
1877-
err() << error() << "non-void options not allowed in option packs" << ignoring( c ) << '\n';
1900+
if( is_verbose() )
1901+
*m_output_destination << error() << "non-void options not allowed in option packs" << ignoring( c ) << '\n';
18781902
continue;
18791903
}
18801904
good &= parse_argument( opt, c );

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [Example 2 (`fallback`, `was_set`, `string`, `multi`)](#example-2-fallback-was_set-string-multi)
1616
- [Example 3 (`description`, `callback`, unnamed parameter)](#example-3-description-callback-unnamed-parameter)
1717
- [Example 4 (more `callback`s, more `fallback`s, `f64`, `to_vector`)](#example-4-more-callbacks-more-fallbacks-f64-to_vector)
18+
- [Miscellaneous functions](#miscellaneous-functions)
1819
- [Defaults](#defaults)
1920
- [Flags](#flags)
2021
- [Third-party libraries](#third-party-libraries)
@@ -302,6 +303,11 @@ successfully parsed 12 which equals 12
302303
successfully parsed NaN which equals nan
303304
( + 12 nan ) = nan
304305
```
306+
### Miscellaneous functions
307+
308+
#### `void po::parser::silent()`
309+
Suppresses all communication via `stderr`. Without this flag, *ProgramOptions.hxx* notifies the user in case of warnings or errors occurring while parsing. For instance, if an option requires an argument of type `i32` and it couldn't be parsed, overflowed or wasn't provided at all, *ProgramOptions.hxx* would print an error saying that the option's argument is invalid and that it hence was ignored.
310+
305311
## Defaults
306312
This small table helps clarifying the defaults for the different kinds of options.
307313

@@ -314,9 +320,6 @@ This small table helps clarifying the defaults for the different kinds of option
314320
## Flags
315321
All flags have to be `#define`d before including *ProgramOptions.hxx*. Different translation units may include *ProgramOptions.hxx* using different flags.
316322

317-
### `#define PROGRAMOPTIONS_SILENT`
318-
Suppresses all communication via `stderr`. Without this flag, *ProgramOptions.hxx* notifies the user in case of warnings or errors occurring while parsing. For instance, if an option requires an argument of type `i32` and it couldn't be parsed or wasn't provided at all, *ProgramOptions.hxx* would print a warning that the option must have an argument and that it hence was ignored.
319-
320323
### `#define PROGRAMOPTIONS_NO_EXCEPTIONS`
321324
Disables all exceptions and thus allows compilation with `-fno-exceptions`. However, incorrect use of the library and unmet preconditions entail `abort()` via `assert(...)`. This flag is implied by `NDEBUG`.
322325

test/callback.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include "arg_provider.hxx"
54

65
TEST_CASE( "callback", "[ProgramOptions]" ) {
76
po::parser parser;
7+
parser.silent();
88

99
po::i32_t a_value{};
1010
po::string_t a_value_str{};

test/defaults.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43

54
TEST_CASE( "defaults", "[ProgramOptions]" ) {
65
po::parser parser;
6+
parser.silent();
7+
78
auto&& _1 = parser[ "abacus" ];
89
auto&& _2 = parser[ "a" ];
910
auto&& _3 = parser[ "" ];

test/errors.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include "arg_provider.hxx"
54

65
TEST_CASE( "errors", "[ProgramOptions]" ) {
76
po::parser parser;
7+
parser.silent();
8+
89
auto&& sum = parser[ "sum" ]
910
.abbreviation( 's' )
1011
.type( po::i64 )

test/exceptions.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#undef NDEBUG
43
#undef PROGRAMOPTIONS_NO_EXCEPTIONS
54
#include <ProgramOptions.hxx>
@@ -8,6 +7,8 @@
87

98
TEST_CASE( "exceptions", "[ProgramOptions]" ) {
109
po::parser parser;
10+
parser.silent();
11+
1112
auto&& a = parser[ "a" ]
1213
.type( po::f32 );
1314
auto&& bc = parser[ "bc" ]

test/integer.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include "arg_provider.hxx"
54

65
TEST_CASE( "integer", "[ProgramOptions]" ) {
76
po::parser parser;
7+
parser.silent();
8+
89
auto&& a = parser[ "a" ]
910
.type( po::i64 );
1011
auto&& b = parser[ "bot" ]

test/option_packs.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include "arg_provider.hxx"
54

65
TEST_CASE( "option_packs", "[ProgramOptions]" ) {
76
po::parser parser;
7+
parser.silent();
8+
89
auto&& g = parser[ "g" ];
910
auto&& h = parser[ "home" ]
1011
.abbreviation( 'h' )

test/str2flt.cxx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include <string>
54
#include <limits>

test/str2int.cxx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include <string>
54
#include <limits>

test/str2uint.cxx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include <string>
54
#include <limits>

test/string.cxx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43
#include "arg_provider.hxx"
54

65
TEST_CASE( "string", "[ProgramOptions]" ) {
76
po::parser parser;
7+
parser.silent();
8+
89
auto&& a = parser[ "a" ]
910
.type( po::string );
1011
auto&& b = parser[ "bot" ]

test/wellformed.cxx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <catch.hpp>
2-
#define PROGRAMOPTIONS_SILENT
32
#include <ProgramOptions.hxx>
43

54
TEST_CASE( "wellformed", "[ProgramOptions]" ) {

0 commit comments

Comments
 (0)