diff --git a/ProgramOptions.hxx b/ProgramOptions.hxx index b771c41..d068567 100644 --- a/ProgramOptions.hxx +++ b/ProgramOptions.hxx @@ -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; @@ -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 ) { diff --git a/test/bind.cxx b/test/bind.cxx index c141206..aa8787c 100644 --- a/test/bind.cxx +++ b/test/bind.cxx @@ -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", @@ -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 ) ); @@ -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 ); @@ -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 ); } }