Skip to content

Commit

Permalink
Fix a bug in bind_container that would occurr when being called with …
Browse files Browse the repository at this point in the history
…std::stack. Now, we use SFINAE to detect the supported pushing function and choose accordingly
  • Loading branch information
Fytch committed May 22, 2018
1 parent 4b002c3 commit 9ef95e9
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 25 deletions.
62 changes: 58 additions & 4 deletions ProgramOptions.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,42 @@ namespace po {
return object -= n;
}

namespace detail {
template< typename container_t, typename... args_t >
class has_push_back_sfinae {
template< typename _container_t, typename... _args_t >
static std::false_type test( ... );
template< typename _container_t, typename... _args_t >
static std::true_type test( decltype( std::declval< _container_t >().push_back( std::declval< _args_t >()... ) )* );

public:
using type = decltype( test< container_t, args_t... >( 0 ) );
};
}
template< typename container_t, typename... args_t >
struct has_push_back : public detail::has_push_back_sfinae< container_t, args_t... >::type {
};
template< typename container_t >
using has_push_back_vt = has_push_back< container_t, typename container_t::value_type >;

namespace detail {
template< typename container_t, typename... args_t >
class has_push_sfinae {
template< typename _container_t, typename... _args_t >
static std::false_type test( ... );
template< typename _container_t, typename... _args_t >
static std::true_type test( decltype( std::declval< _container_t >().push( std::declval< _args_t >()... ) )* );

public:
using type = decltype( test< container_t, args_t... >( 0 ) );
};
}
template< typename container_t, typename... args_t >
struct has_push : public detail::has_push_sfinae< container_t, args_t... >::type {
};
template< typename container_t >
using has_push_vt = has_push< container_t, typename container_t::value_type >;

class option {
char m_abbreviation = '\0';
std::string m_description;
Expand Down Expand Up @@ -1651,14 +1687,32 @@ namespace po {
return *this;
}

template< typename T >
option& bind_container( T& target ) {
using value_type = typename T::value_type;
template< typename container_t, typename invocable_t >
option& bind_container( container_t& container, invocable_t inserter ) {
using value_type = typename container_t::value_type;
type( type2vt< value_type >::value );
multi();
callback( [ &target ]( value_type const& x ){ target.push_back( x ); } );
callback( [ &container, inserter ]( value_type const& x ){ inserter( container, x ); } );
return *this;
}
template< typename container_t >
option& bind_container( container_t& container, typename std::enable_if< has_push_back_vt< container_t >::value >::type* = nullptr ) {
using value_type = typename container_t::value_type;
return bind_container( container,
[]( container_t& container, value_type const& value ){
container.push_back( value );
}
);
}
template< typename container_t >
option& bind_container( container_t& container, typename std::enable_if< has_push_vt< container_t >::value >::type* = nullptr ) {
using value_type = typename container_t::value_type;
return bind_container( container,
[]( container_t& container, value_type const& value ){
container.push( value );
}
);
}

template< typename T >
option& bind( T& target ) {
Expand Down
74 changes: 53 additions & 21 deletions test/bind.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,70 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
parser.silent();

std::string a;
std::int32_t b{};
std::int64_t c{};
std::uint32_t d{};
std::uint64_t e{};
// float f{};
// double g{};
std::vector< std::int32_t > h;
std::deque< std::string > i;

auto&& a_opt = parser[ "a" ].bind( a );
auto&& b_opt = parser[ "b" ].bind( b );
auto&& c_opt = parser[ "c" ].bind( c );
auto&& d_opt = parser[ "d" ].bind( d );
auto&& e_opt = parser[ "e" ].bind( e );
// auto&& f_opt = parser[ "f" ].bind( f );
// auto&& g_opt = parser[ "g" ].bind( g );
auto&& h_opt = parser[ "h" ].bind( h );
auto&& i_opt = parser[ "i" ].bind( i );

REQUIRE( a_opt.is_single() );
REQUIRE( a_opt.get_type() == po::string );

std::int32_t b{};
auto&& b_opt = parser[ "b" ].bind( b );
REQUIRE( b_opt.is_single() );
REQUIRE( b_opt.get_type() == po::i32 );

std::int64_t c{};
auto&& c_opt = parser[ "c" ].bind( c );
REQUIRE( c_opt.is_single() );
REQUIRE( c_opt.get_type() == po::i64 );

std::uint32_t d{};
auto&& d_opt = parser[ "d" ].bind( d );
REQUIRE( d_opt.is_single() );
REQUIRE( d_opt.get_type() == po::u32 );

std::uint64_t e{};
auto&& e_opt = parser[ "e" ].bind( e );
REQUIRE( e_opt.is_single() );
REQUIRE( e_opt.get_type() == po::u64 );

// float f{};
// auto&& f_opt = parser[ "f" ].bind( f );
// REQUIRE( f_opt.is_single() );
// REQUIRE( f_opt.get_type() == po::f32 );

// double g{};
// auto&& g_opt = parser[ "g" ].bind( g );
// REQUIRE( g_opt.is_single() );
// REQUIRE( g_opt.get_type() == po::f64 );

std::vector< std::int32_t > h;
auto&& h_opt = parser[ "h" ].bind( h );
REQUIRE( h_opt.is_multi() );
REQUIRE( h_opt.get_type() == po::i32 );

std::deque< std::string > i;
auto&& i_opt = parser[ "i" ].bind( i );
REQUIRE( i_opt.is_multi() );
REQUIRE( i_opt.get_type() == po::string );

std::list< std::int32_t > j;
auto&& j_opt = parser[ "j" ].bind( j );
REQUIRE( j_opt.is_multi() );
REQUIRE( j_opt.get_type() == po::i32 );

std::stack< std::int64_t > k;
auto&& k_opt = parser[ "k" ].bind( k );
REQUIRE( k_opt.is_multi() );
REQUIRE( k_opt.get_type() == po::i64 );

std::queue< std::uint32_t > l;
auto&& l_opt = parser[ "l" ].bind( l );
REQUIRE( l_opt.is_multi() );
REQUIRE( l_opt.get_type() == po::u32 );

std::priority_queue< std::uint64_t > m;
auto&& m_opt = parser[ "m" ].bind( m );
REQUIRE( m_opt.is_multi() );
REQUIRE( m_opt.get_type() == po::u64 );

SECTION( "good scenario" ) {
const arg_provider A {
"/Test",
Expand All @@ -53,8 +79,10 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
"-h 42",
"-i" "foo",
"-i" "bar",
"-b 36",
"-b 42",
"-k 2",
"-k 7",
"-b 3",
"-b 4",
};

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

REQUIRE( b_opt.count() == 1 );
CHECK( b == 42 );
CHECK( b == 4 );

REQUIRE( h_opt.count() == 2 );
REQUIRE( h.size() == 2 );
Expand All @@ -75,5 +103,9 @@ TEST_CASE( "bind", "[ProgramOptions]" ) {
REQUIRE( i.size() == 2 );
CHECK( i[ 0 ] == "foo" );
CHECK( i[ 1 ] == "bar" );

REQUIRE( k_opt.count() == 2 );
REQUIRE( k.size() == 2 );
CHECK( k.top() == 7 );
}
}

0 comments on commit 9ef95e9

Please sign in to comment.