diff --git a/include/fc/rpc/api_connection.hpp b/include/fc/rpc/api_connection.hpp index 71eaf8067..281f1d23e 100644 --- a/include/fc/rpc/api_connection.hpp +++ b/include/fc/rpc/api_connection.hpp @@ -44,13 +44,18 @@ namespace fc { } template - R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e, uint32_t max_depth ) + R call_generic( const std::function& f, variants::const_iterator a0, + variants::const_iterator e, uint32_t max_depth ) { bool optional_args = all_optionals, std::decay_t...>::value; FC_ASSERT( a0 != e || optional_args ); FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" ); - auto arg = (a0 == e)? std::decay_t() : a0->as>(max_depth - 1); - return call_generic( bind_first_arg( f, arg ), a0+1, e, max_depth - 1 ); + if (a0==e) + return call_generic( bind_first_arg( f, std::decay_t() ), a0, + e, max_depth - 1 ); + auto arg = a0->as>(max_depth - 1); + return call_generic( bind_first_arg( f, std::move(arg) ), a0+1, e, + max_depth - 1 ); } template @@ -180,13 +185,18 @@ namespace fc { } template - R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e, uint32_t max_depth ) + R call_generic( const std::function& f, variants::const_iterator a0, + variants::const_iterator e, uint32_t max_depth ) { bool optional_args = detail::all_optionals, std::decay_t...>::value; FC_ASSERT( a0 != e || optional_args, "too few arguments passed to method" ); FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" ); - auto arg = (a0 == e)? std::decay_t() : a0->as>(max_depth - 1); - return call_generic( this->bind_first_arg( f, arg ), a0+1, e, max_depth - 1 ); + if (a0==e) + return call_generic( this->bind_first_arg( f, std::decay_t() ), a0, + e, max_depth - 1 ); + auto arg = a0->as>(max_depth - 1); + return call_generic( this->bind_first_arg( f, std::move(arg) ), a0+1, e, + max_depth - 1 ); } struct api_visitor diff --git a/tests/api_tests.cpp b/tests/api_tests.cpp index dff535077..ef42b8d9f 100644 --- a/tests/api_tests.cpp +++ b/tests/api_tests.cpp @@ -39,8 +39,12 @@ class optionals_api const fc::optional& third ) { return fc::json::to_string(fc::variants{first, {second, 2}, {third, 2}}); } + std::string bar( fc::optional first, fc::optional second, + fc::optional third ) { + return fc::json::to_string(fc::variants{{first,2}, {second, 2}, {third, 2}}); + } }; -FC_API( optionals_api, (foo) ); +FC_API( optionals_api, (foo)(bar) ); using namespace fc; @@ -106,6 +110,12 @@ BOOST_AUTO_TEST_CASE(optionals_test) { BOOST_CHECK_EQUAL(oapi->foo("a", "b", "c"), "[\"a\",\"b\",\"c\"]"); BOOST_CHECK_EQUAL(oapi->foo("a", {}, "c"), "[\"a\",null,\"c\"]"); + BOOST_CHECK_EQUAL(oapi->bar(), "[null,null,null]"); + BOOST_CHECK_EQUAL(oapi->bar("a"), "[\"a\",null,null]"); + BOOST_CHECK_EQUAL(oapi->bar("a", "b"), "[\"a\",\"b\",null]"); + BOOST_CHECK_EQUAL(oapi->bar("a", "b", "c"), "[\"a\",\"b\",\"c\"]"); + BOOST_CHECK_EQUAL(oapi->bar("a", {}, "c"), "[\"a\",null,\"c\"]"); + auto server = std::make_shared(); server->on_connection([&]( const websocket_connection_ptr& c ){ auto wsc = std::make_shared(c, MAX_DEPTH); @@ -119,7 +129,6 @@ BOOST_AUTO_TEST_CASE(optionals_test) { auto client = std::make_shared(); auto con = client->connect( "ws://localhost:" + std::to_string(listen_port) ); - server->stop_listening(); auto apic = std::make_shared(con, MAX_DEPTH); auto remote_optionals = apic->get_remote_api(); @@ -128,6 +137,37 @@ BOOST_AUTO_TEST_CASE(optionals_test) { BOOST_CHECK_EQUAL(remote_optionals->foo("a", "b", "c"), "[\"a\",\"b\",\"c\"]"); BOOST_CHECK_EQUAL(remote_optionals->foo("a", {}, "c"), "[\"a\",null,\"c\"]"); + BOOST_CHECK_EQUAL(remote_optionals->bar(), "[null,null,null]"); + BOOST_CHECK_EQUAL(remote_optionals->bar("a"), "[\"a\",null,null]"); + BOOST_CHECK_EQUAL(remote_optionals->bar("a", "b"), "[\"a\",\"b\",null]"); + BOOST_CHECK_EQUAL(remote_optionals->bar("a", "b", "c"), "[\"a\",\"b\",\"c\"]"); + BOOST_CHECK_EQUAL(remote_optionals->bar("a", {}, "c"), "[\"a\",null,\"c\"]"); + + auto client2 = std::make_shared(); + auto con2 = client2->connect( "ws://localhost:" + std::to_string(listen_port) ); + string response; + con2->on_message_handler([&](const std::string& s){ + response = s; + }); + + con2->send_message( "{\"id\":1,\"method\":\"call\",\"params\":[0,\"bar\",[\"a\",\"b\",\"c\"]]}" ); + fc::usleep(fc::milliseconds(50)); + BOOST_CHECK_EQUAL( response, "{\"id\":1,\"result\":\"[\\\"a\\\",\\\"b\\\",\\\"c\\\"]\"}" ); + + con2->send_message( "{\"id\":2,\"method\":\"call\",\"params\":[0,\"bar\",[\"a\",\"b\"]]}" ); + fc::usleep(fc::milliseconds(50)); + BOOST_CHECK_EQUAL( response, "{\"id\":2,\"result\":\"[\\\"a\\\",\\\"b\\\",null]\"}" ); + + con2->send_message( "{\"id\":3,\"method\":\"call\",\"params\":[0,\"bar\",[\"a\"]]}" ); + fc::usleep(fc::milliseconds(50)); + BOOST_CHECK_EQUAL( response, "{\"id\":3,\"result\":\"[\\\"a\\\",null,null]\"}" ); + + con2->send_message( "{\"id\":4,\"method\":\"call\",\"params\":[0,\"bar\",[]]}" ); + fc::usleep(fc::milliseconds(50)); + BOOST_CHECK_EQUAL( response, "{\"id\":4,\"result\":\"[null,null,null]\"}" ); + + server->stop_listening(); + client->synchronous_close(); server->close(); fc::usleep(fc::milliseconds(50));