Skip to content

Commit 9ef95e9

Browse files
author
Fytch
committed
Fix a bug in bind_container that would occurr when being called with std::stack. Now, we use SFINAE to detect the supported pushing function and choose accordingly
1 parent 4b002c3 commit 9ef95e9

File tree

2 files changed

+111
-25
lines changed

2 files changed

+111
-25
lines changed

ProgramOptions.hxx

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,42 @@ namespace po {
11661166
return object -= n;
11671167
}
11681168

1169+
namespace detail {
1170+
template< typename container_t, typename... args_t >
1171+
class has_push_back_sfinae {
1172+
template< typename _container_t, typename... _args_t >
1173+
static std::false_type test( ... );
1174+
template< typename _container_t, typename... _args_t >
1175+
static std::true_type test( decltype( std::declval< _container_t >().push_back( std::declval< _args_t >()... ) )* );
1176+
1177+
public:
1178+
using type = decltype( test< container_t, args_t... >( 0 ) );
1179+
};
1180+
}
1181+
template< typename container_t, typename... args_t >
1182+
struct has_push_back : public detail::has_push_back_sfinae< container_t, args_t... >::type {
1183+
};
1184+
template< typename container_t >
1185+
using has_push_back_vt = has_push_back< container_t, typename container_t::value_type >;
1186+
1187+
namespace detail {
1188+
template< typename container_t, typename... args_t >
1189+
class has_push_sfinae {
1190+
template< typename _container_t, typename... _args_t >
1191+
static std::false_type test( ... );
1192+
template< typename _container_t, typename... _args_t >
1193+
static std::true_type test( decltype( std::declval< _container_t >().push( std::declval< _args_t >()... ) )* );
1194+
1195+
public:
1196+
using type = decltype( test< container_t, args_t... >( 0 ) );
1197+
};
1198+
}
1199+
template< typename container_t, typename... args_t >
1200+
struct has_push : public detail::has_push_sfinae< container_t, args_t... >::type {
1201+
};
1202+
template< typename container_t >
1203+
using has_push_vt = has_push< container_t, typename container_t::value_type >;
1204+
11691205
class option {
11701206
char m_abbreviation = '\0';
11711207
std::string m_description;
@@ -1651,14 +1687,32 @@ namespace po {
16511687
return *this;
16521688
}
16531689

1654-
template< typename T >
1655-
option& bind_container( T& target ) {
1656-
using value_type = typename T::value_type;
1690+
template< typename container_t, typename invocable_t >
1691+
option& bind_container( container_t& container, invocable_t inserter ) {
1692+
using value_type = typename container_t::value_type;
16571693
type( type2vt< value_type >::value );
16581694
multi();
1659-
callback( [ &target ]( value_type const& x ){ target.push_back( x ); } );
1695+
callback( [ &container, inserter ]( value_type const& x ){ inserter( container, x ); } );
16601696
return *this;
16611697
}
1698+
template< typename container_t >
1699+
option& bind_container( container_t& container, typename std::enable_if< has_push_back_vt< container_t >::value >::type* = nullptr ) {
1700+
using value_type = typename container_t::value_type;
1701+
return bind_container( container,
1702+
[]( container_t& container, value_type const& value ){
1703+
container.push_back( value );
1704+
}
1705+
);
1706+
}
1707+
template< typename container_t >
1708+
option& bind_container( container_t& container, typename std::enable_if< has_push_vt< container_t >::value >::type* = nullptr ) {
1709+
using value_type = typename container_t::value_type;
1710+
return bind_container( container,
1711+
[]( container_t& container, value_type const& value ){
1712+
container.push( value );
1713+
}
1714+
);
1715+
}
16621716

16631717
template< typename T >
16641718
option& bind( T& target ) {

test/bind.cxx

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,70 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
77
parser.silent();
88

99
std::string a;
10-
std::int32_t b{};
11-
std::int64_t c{};
12-
std::uint32_t d{};
13-
std::uint64_t e{};
14-
// float f{};
15-
// double g{};
16-
std::vector< std::int32_t > h;
17-
std::deque< std::string > i;
18-
1910
auto&& a_opt = parser[ "a" ].bind( a );
20-
auto&& b_opt = parser[ "b" ].bind( b );
21-
auto&& c_opt = parser[ "c" ].bind( c );
22-
auto&& d_opt = parser[ "d" ].bind( d );
23-
auto&& e_opt = parser[ "e" ].bind( e );
24-
// auto&& f_opt = parser[ "f" ].bind( f );
25-
// auto&& g_opt = parser[ "g" ].bind( g );
26-
auto&& h_opt = parser[ "h" ].bind( h );
27-
auto&& i_opt = parser[ "i" ].bind( i );
28-
2911
REQUIRE( a_opt.is_single() );
3012
REQUIRE( a_opt.get_type() == po::string );
13+
14+
std::int32_t b{};
15+
auto&& b_opt = parser[ "b" ].bind( b );
3116
REQUIRE( b_opt.is_single() );
3217
REQUIRE( b_opt.get_type() == po::i32 );
18+
19+
std::int64_t c{};
20+
auto&& c_opt = parser[ "c" ].bind( c );
3321
REQUIRE( c_opt.is_single() );
3422
REQUIRE( c_opt.get_type() == po::i64 );
23+
24+
std::uint32_t d{};
25+
auto&& d_opt = parser[ "d" ].bind( d );
3526
REQUIRE( d_opt.is_single() );
3627
REQUIRE( d_opt.get_type() == po::u32 );
28+
29+
std::uint64_t e{};
30+
auto&& e_opt = parser[ "e" ].bind( e );
3731
REQUIRE( e_opt.is_single() );
3832
REQUIRE( e_opt.get_type() == po::u64 );
33+
34+
// float f{};
35+
// auto&& f_opt = parser[ "f" ].bind( f );
3936
// REQUIRE( f_opt.is_single() );
4037
// REQUIRE( f_opt.get_type() == po::f32 );
38+
39+
// double g{};
40+
// auto&& g_opt = parser[ "g" ].bind( g );
4141
// REQUIRE( g_opt.is_single() );
4242
// REQUIRE( g_opt.get_type() == po::f64 );
43+
44+
std::vector< std::int32_t > h;
45+
auto&& h_opt = parser[ "h" ].bind( h );
4346
REQUIRE( h_opt.is_multi() );
4447
REQUIRE( h_opt.get_type() == po::i32 );
48+
49+
std::deque< std::string > i;
50+
auto&& i_opt = parser[ "i" ].bind( i );
4551
REQUIRE( i_opt.is_multi() );
4652
REQUIRE( i_opt.get_type() == po::string );
4753

54+
std::list< std::int32_t > j;
55+
auto&& j_opt = parser[ "j" ].bind( j );
56+
REQUIRE( j_opt.is_multi() );
57+
REQUIRE( j_opt.get_type() == po::i32 );
58+
59+
std::stack< std::int64_t > k;
60+
auto&& k_opt = parser[ "k" ].bind( k );
61+
REQUIRE( k_opt.is_multi() );
62+
REQUIRE( k_opt.get_type() == po::i64 );
63+
64+
std::queue< std::uint32_t > l;
65+
auto&& l_opt = parser[ "l" ].bind( l );
66+
REQUIRE( l_opt.is_multi() );
67+
REQUIRE( l_opt.get_type() == po::u32 );
68+
69+
std::priority_queue< std::uint64_t > m;
70+
auto&& m_opt = parser[ "m" ].bind( m );
71+
REQUIRE( m_opt.is_multi() );
72+
REQUIRE( m_opt.get_type() == po::u64 );
73+
4874
SECTION( "good scenario" ) {
4975
const arg_provider A {
5076
"/Test",
@@ -53,8 +79,10 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
5379
"-h 42",
5480
"-i" "foo",
5581
"-i" "bar",
56-
"-b 36",
57-
"-b 42",
82+
"-k 2",
83+
"-k 7",
84+
"-b 3",
85+
"-b 4",
5886
};
5987

6088
CHECK( parser( A.argc, A.argv ) );
@@ -63,7 +91,7 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
6391
CHECK( a == "hello" );
6492

6593
REQUIRE( b_opt.count() == 1 );
66-
CHECK( b == 42 );
94+
CHECK( b == 4 );
6795

6896
REQUIRE( h_opt.count() == 2 );
6997
REQUIRE( h.size() == 2 );
@@ -75,5 +103,9 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
75103
REQUIRE( i.size() == 2 );
76104
CHECK( i[ 0 ] == "foo" );
77105
CHECK( i[ 1 ] == "bar" );
106+
107+
REQUIRE( k_opt.count() == 2 );
108+
REQUIRE( k.size() == 2 );
109+
CHECK( k.top() == 7 );
78110
}
79111
}

0 commit comments

Comments
 (0)