From ea1a77b98b6e7955c5cc27e10d2a9b297e80e9f8 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 17 Jun 2021 13:52:49 -0400 Subject: [PATCH 1/5] Replace AUTO_RETURN uses with decltype(auto) Can't use decltype(auto) everywhere because it doesn't seem to work in SFINAE contexts. Substitution failure leads to compile errors instead of just silently dropping functions from overload set. --- include/mp/proxy-types.h | 29 ++++++++++++++++------------- include/mp/proxy.h | 6 ++++-- include/mp/util.h | 12 +++++++----- src/mp/gen.cpp | 4 ++-- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 410de123..137dbed3 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -44,8 +44,7 @@ struct StructField template auto has() const -> typename std::enable_if::type { return true; } template auto want() const -> typename std::enable_if::type { return A::getWant(m_struct); } template auto want() const -> typename std::enable_if::type { return true; } - - template auto set(Args&&... args) const -> AUTO_RETURN(A::set(this->m_struct, std::forward(args)...)) + template decltype(auto) set(Args&&... args) const { return A::set(this->m_struct, std::forward(args)...); } template auto init(Args&&... args) const -> AUTO_RETURN(A::init(this->m_struct, std::forward(args)...)) template auto setHas() const -> typename std::enable_if::type { return A::setHas(m_struct); } template auto setHas() const -> typename std::enable_if::type { } @@ -521,7 +520,7 @@ struct ProxyCallFn InvokeContext m_proxy; template - auto operator()(CallParams&&... params) -> AUTO_RETURN(this->m_proxy->call(std::forward(params)...)) + decltype(auto) operator()(CallParams&&... params) { return this->m_proxy->call(std::forward(params)...); } }; template @@ -762,10 +761,10 @@ struct ListOutput<::capnp::List> size_t m_index; // clang-format off - auto get() const -> AUTO_RETURN(this->m_builder[this->m_index]) - auto init() const -> AUTO_RETURN(this->m_builder[this->m_index]) - template auto set(Arg&& arg) const -> AUTO_RETURN(static_cast(this->m_builder).set(m_index, std::forward(arg))) - template auto init(Arg&& arg) const -> AUTO_RETURN(static_cast(this->m_builder).init(m_index, std::forward(arg))) + decltype(auto) get() const { return this->m_builder[this->m_index]; } + decltype(auto) init() const { return this->m_builder[this->m_index]; } + template decltype(auto) set(Arg&& arg) const { return static_cast(this->m_builder).set(m_index, std::forward(arg)); } + template decltype(auto) init(Arg&& arg) const { return static_cast(this->m_builder).init(m_index, std::forward(arg)); } // clang-format on }; @@ -1248,10 +1247,12 @@ struct ServerCall { // FIXME: maybe call call_context.releaseParams() template - auto invoke(ServerContext& server_context, TypeList<>, Args&&... args) const -> AUTO_RETURN( - ProxyServerMethodTraits::invoke( + decltype(auto) invoke(ServerContext& server_context, TypeList<>, Args&&... args) const + { + return ProxyServerMethodTraits::invoke( server_context, - std::forward(args)...)) + std::forward(args)...); + } }; struct ServerDestroy @@ -1318,13 +1319,15 @@ struct ServerField : Parent const Parent& parent() const { return *this; } template - auto invoke(ServerContext& server_context, ArgTypes, Args&&... args) const - -> AUTO_RETURN(CallPassField(Priority<2>(), + decltype(auto) invoke(ServerContext& server_context, ArgTypes, Args&&... args) const + { + return CallPassField(Priority<2>(), typename Split::First(), server_context, this->parent(), typename Split::Second(), - std::forward(args)...)) + std::forward(args)...); + } }; template diff --git a/include/mp/proxy.h b/include/mp/proxy.h index 602c4ae4..6c0645e7 100644 --- a/include/mp/proxy.h +++ b/include/mp/proxy.h @@ -163,8 +163,10 @@ struct ProxyMethodTraits::impl)> { template - static auto invoke(ServerContext& server_context, Args&&... args) -> AUTO_RETURN( - (server_context.proxy_server.m_impl.get()->*ProxyMethod::impl)(std::forward(args)...)) + static decltype(auto) invoke(ServerContext& server_context, Args&&... args) + { + return (server_context.proxy_server.m_impl.get()->*ProxyMethod::impl)(std::forward(args)...); + } }; //! Customizable (through template specialization) traits class used in generated ProxyClient implementations from diff --git a/include/mp/util.h b/include/mp/util.h index 6addb018..9126fcca 100644 --- a/include/mp/util.h +++ b/include/mp/util.h @@ -102,7 +102,7 @@ struct ComposeFn Fn2&& fn2; template - auto operator()(Args&&... args) -> AUTO_RETURN(this->fn1(this->fn2(std::forward(args)...))) + decltype(auto) operator()(Args&&... args) { return this->fn1(this->fn2(std::forward(args)...)); } }; //! Bound function. See Bind() below. @@ -116,8 +116,10 @@ struct BoundFn, TypeList> Fn&& m_fn; template - auto operator()(BoundArgs&... bound_args, FreeArgs&&... free_args) - -> AUTO_RETURN(this->m_fn(bound_args..., std::forward(free_args)...)) + decltype(auto) operator()(BoundArgs&... bound_args, FreeArgs&&... free_args) + { + return this->m_fn(bound_args..., std::forward(free_args)...); + } }; //! Specialization of above for recursive case. @@ -130,7 +132,7 @@ struct BoundFn, TypeList> BoundFn(Fn& fn, BindArg& bind_arg, BindArgs&... bind_args) : Base{fn, bind_args...}, m_bind_arg(bind_arg) {} - // Use std::result_of instead of AUTO_RETURN to work around gcc bug + // Use std::result_of instead of decltype return to work around gcc bug // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83249 template auto operator()(BoundArgs&... bound_args, FreeArgs&&... free_args) -> @@ -167,7 +169,7 @@ struct BoundTupleFn { Fn& m_fn; template - auto operator()(Params&&... params) -> AUTO_RETURN(this->m_fn(std::forward_as_tuple(params...))) + decltype(auto) operator()(Params&&... params) { return this->m_fn(std::forward_as_tuple(params...)); } }; //! Bind tuple argument to function. Arguments passed to the returned function diff --git a/src/mp/gen.cpp b/src/mp/gen.cpp index 13071fba..ab3649ba 100644 --- a/src/mp/gen.cpp +++ b/src/mp/gen.cpp @@ -317,8 +317,8 @@ void Generate(kj::StringPtr src_prefix, auto field_name = field.getProto().getName(); auto member_name = field_name; GetAnnotationText(field.getProto(), NAME_ANNOTATION_ID, &member_name); - inl << " static auto get(std::integral_constant) -> AUTO_RETURN(" - << "&" << proxied_class_type << "::" << member_name << ")\n"; + inl << " static decltype(auto) get(std::integral_constant) { return " + << "&" << proxied_class_type << "::" << member_name << "; }\n"; ++i; } inl << " static constexpr size_t fields = " << i << ";\n"; From 1d005053b0803564d46d5821e99a7efa91121472 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 23 Dec 2021 13:45:11 -0500 Subject: [PATCH 2/5] Replace output.init AUTO_RETURN uses with decltype(auto) Since decltype(auto) doesn't works inside SFINAE context, need to replace enable_if with enable_if --- include/mp/proxy-types.h | 4 ++-- src/mp/gen.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 137dbed3..ecf4b2a7 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -45,7 +45,7 @@ struct StructField template auto want() const -> typename std::enable_if::type { return A::getWant(m_struct); } template auto want() const -> typename std::enable_if::type { return true; } template decltype(auto) set(Args&&... args) const { return A::set(this->m_struct, std::forward(args)...); } - template auto init(Args&&... args) const -> AUTO_RETURN(A::init(this->m_struct, std::forward(args)...)) + template decltype(auto) init(Args&&... args) const { return A::init(this->m_struct, std::forward(args)...); } template auto setHas() const -> typename std::enable_if::type { return A::setHas(m_struct); } template auto setHas() const -> typename std::enable_if::type { } template auto setWant() const -> typename std::enable_if::type { return A::setWant(m_struct); } @@ -58,7 +58,7 @@ void CustomBuildField(TypeList<>, Priority<1>, ClientInvokeContext& invoke_context, Output&& output, - typename std::enable_if::value>::type* enable = nullptr) + typename std::enable_if::value>::type* enable = nullptr) { auto& connection = invoke_context.connection; auto& thread_context = invoke_context.thread_context; diff --git a/src/mp/gen.cpp b/src/mp/gen.cpp index ab3649ba..1964d4a8 100644 --- a/src/mp/gen.cpp +++ b/src/mp/gen.cpp @@ -244,8 +244,8 @@ void Generate(kj::StringPtr src_prefix, accessors << " template static bool has(S&& s) { return s.has" << cap << "(); }\n"; accessors << " template static void set(S&& s, A&& a) { s.set" << cap << "(std::forward(a)); }\n"; - accessors << " template static auto init(S&& s, A&&... a) -> AUTO_RETURN(s.init" - << cap << "(std::forward(a)...))\n"; + accessors << " template static decltype(auto) init(S&& s, A&&... a) { return s.init" + << cap << "(std::forward(a)...); }\n"; accessors << " template static bool getWant(S&& s) { return s.getWant" << cap << "(); }\n"; accessors << " template static void setWant(S&& s) { s.setWant" << cap << "(true); }\n"; accessors << " template static bool getHas(S&& s) { return s.getHas" << cap << "(); }\n"; From 41db49c7bc1bda05805a507682939886ea5d5270 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 23 Dec 2021 15:04:05 -0500 Subject: [PATCH 3/5] Consolidate PassField function to remove AUTO_RETURN uses --- include/mp/proxy-types.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index ecf4b2a7..342eae37 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -105,8 +105,10 @@ kj::Promise JoinPromises(kj::Promise&& prom1, kj::Promise&& prom2) return prom1.then(kj::mvCapture(prom2, [](kj::Promise prom2) { return prom2; })); } +//! PassField override for mp.Context arguments. Return asynchronously and call +//! function on other thread found in context. template -auto PassField(TypeList<>, ServerContext& server_context, const Fn& fn, const Args&... args) -> +auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn& fn, const Args&... args) -> typename std::enable_if< std::is_same::value, kj::Promise>::type @@ -977,8 +979,9 @@ void CustomBuildField(TypeList local_type, BuildOne<0>(local_type, invoke_context, output.init(), value); } +//! PassField override for C++ pointer arguments. template -void PassField(TypeList, ServerContext& server_context, const Fn& fn, Args&&... args) +void PassField(Priority<1>, TypeList, ServerContext& server_context, const Fn& fn, Args&&... args) { const auto& params = server_context.call_context.getParams(); const auto& input = Make(params); @@ -1001,8 +1004,9 @@ void PassField(TypeList, ServerContext& server_context, const Fn& fn Make(results), param); } +//! PassField override for callable interface reference arguments. template -auto PassField(TypeList, ServerContext& server_context, Fn&& fn, Args&&... args) +auto PassField(Priority<1>, TypeList, ServerContext& server_context, Fn&& fn, Args&&... args) -> Require { // Just create a temporary ProxyClient if argument is a reference to an @@ -1050,8 +1054,9 @@ void MaybeSetWant(LocalTypes, Priority<0>, Args&&...) { } +//! Default PassField implementation calling MaybeReadField/MaybeBuildField. template -void DefaultPassField(TypeList, ServerContext& server_context, Fn&& fn, Args&&... args) +void PassField(Priority<0>, TypeList, ServerContext& server_context, Fn&& fn, Args&&... args) { InvokeContext& invoke_context = server_context; using ArgType = RemoveCvRef; @@ -1102,8 +1107,9 @@ decltype(auto) CustomReadField(TypeList<>, invoke_context.connection.m_thread_map = input.get(); } +//! PassField override for mp.ThreadMap arguments. template -auto PassField(TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args) -> typename std::enable_if< +auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args) -> typename std::enable_if< std::is_same::value>::type { const auto& params = server_context.call_context.getParams(); @@ -1300,16 +1306,12 @@ struct ServerExcept : Parent template void CustomPassField(); -// clang-format off +//! PassField override calling CustomPassField function, if it exists. template -auto CallPassField(Priority<2>, Args&&... args) -> AUTO_RETURN(CustomPassField(std::forward(args)...)); - -template -auto CallPassField(Priority<1>, Args&&... args) -> AUTO_RETURN(PassField(std::forward(args)...)); - -template -auto CallPassField(Priority<0>, Args&&... args) -> AUTO_RETURN(DefaultPassField(std::forward(args)...)); -// clang-format on +auto PassField(Priority<2>, Args&&... args) -> decltype(CustomPassField(std::forward(args)...)) +{ + return CustomPassField(std::forward(args)...); +}; template struct ServerField : Parent @@ -1321,7 +1323,7 @@ struct ServerField : Parent template decltype(auto) invoke(ServerContext& server_context, ArgTypes, Args&&... args) const { - return CallPassField(Priority<2>(), + return PassField(Priority<2>(), typename Split::First(), server_context, this->parent(), From 3f388cf0649ba8bb29bb991c1e54dd42d8c8835e Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 23 Dec 2021 15:56:36 -0500 Subject: [PATCH 4/5] Inline last AUTO_RETURN uses and remove macro Need to use decltype(expression) instead of decltype(auto) for get() methods, because they are used in SFINAE contexts to detect whether function arguments are input or output arguments. For output-only arguments, getParams().getXXX() method won't exist, so get method shouldn't be declared. For input-only arguments, getResult().getXXX() method won't exist, so get method shouldn't be declared. --- include/mp/proxy-types.h | 2 +- include/mp/util.h | 11 ----------- src/mp/gen.cpp | 2 +- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 342eae37..f8bd5ed5 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -38,7 +38,7 @@ struct StructField Struct& m_struct; // clang-format off - template auto get() const -> AUTO_RETURN(A::get(this->m_struct)) + template auto get() const -> decltype(A::get(this->m_struct)) { return A::get(this->m_struct); } template auto has() const -> typename std::enable_if::type { return A::getHas(m_struct); } template auto has() const -> typename std::enable_if::type { return A::has(m_struct); } template auto has() const -> typename std::enable_if::type { return true; } diff --git a/include/mp/util.h b/include/mp/util.h index 9126fcca..ccbea6c9 100644 --- a/include/mp/util.h +++ b/include/mp/util.h @@ -55,17 +55,6 @@ namespace mp { //! } //! }; -// C++11 workaround for C++14 auto return functions -// (http://en.cppreference.com/w/cpp/language/template_argument_deduction#auto-returning_functions) -#define AUTO_DO_RETURN(pre, x) \ - decltype(x) \ - { \ - pre; \ - return x; \ - } - -#define AUTO_RETURN(x) AUTO_DO_RETURN(, x) - //! Type holding a list of types. //! //! Example: diff --git a/src/mp/gen.cpp b/src/mp/gen.cpp index 1964d4a8..c78d6ffa 100644 --- a/src/mp/gen.cpp +++ b/src/mp/gen.cpp @@ -240,7 +240,7 @@ void Generate(kj::StringPtr src_prefix, std::string cap = Cap(name); accessors << "struct " << cap << "\n"; accessors << "{\n"; - accessors << " template static auto get(S&& s) -> AUTO_RETURN(s.get" << cap << "())\n"; + accessors << " template static auto get(S&& s) -> decltype(s.get" << cap << "()) { return s.get" << cap << "(); }\n"; accessors << " template static bool has(S&& s) { return s.has" << cap << "(); }\n"; accessors << " template static void set(S&& s, A&& a) { s.set" << cap << "(std::forward(a)); }\n"; From cf7ebfe68ddbd4ec6908cafc9c042dd73e1ee5bd Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 23 Dec 2021 16:15:13 -0500 Subject: [PATCH 5/5] Replace ThreadMap Passfield override with generic count(0) override This override is useful for other count(0) parameters, not just ThreadMap, so no reason it needs to be specific to ThreadMap. This commit follows up earlier commit cleaning up and unifying PassField functions. --- include/mp/proxy-types.h | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index f8bd5ed5..0221784c 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -108,7 +108,7 @@ kj::Promise JoinPromises(kj::Promise&& prom1, kj::Promise&& prom2) //! PassField override for mp.Context arguments. Return asynchronously and call //! function on other thread found in context. template -auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn& fn, const Args&... args) -> +auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args) -> typename std::enable_if< std::is_same::value, kj::Promise>::type @@ -1078,6 +1078,18 @@ void PassField(Priority<0>, TypeList, ServerContext& server_context, Make(results), *param); } +//! Default PassField implementation for count(0) arguments, calling ReadField/BuildField +template +void PassField(Priority<0>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args) +{ + const auto& params = server_context.call_context.getParams(); + const auto& input = Make(params); + ReadField(TypeList<>(), server_context, input); + fn.invoke(server_context, std::forward(args)...); + auto&& results = server_context.call_context.getResults(); + BuildField(TypeList<>(), server_context, Make(results)); +} + template <> struct ProxyServer final : public virtual ThreadMap::Server { @@ -1107,19 +1119,6 @@ decltype(auto) CustomReadField(TypeList<>, invoke_context.connection.m_thread_map = input.get(); } -//! PassField override for mp.ThreadMap arguments. -template -auto PassField(Priority<1>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args) -> typename std::enable_if< - std::is_same::value>::type -{ - const auto& params = server_context.call_context.getParams(); - const auto& input = Make(params); - ReadField(TypeList<>(), server_context, input); - fn.invoke(server_context, std::forward(args)...); - auto&& results = server_context.call_context.getResults(); - BuildField(TypeList<>(), server_context, Make(results)); -} - template struct IterateFieldsHelper {