Skip to content

Commit 36cfdeb

Browse files
authored
[orc-rt] Add method-wrapper utils for use with WrapperFunction::handle. (#162035)
WrapperFunction::handleWithAsyncMethod can be used to wrap asynchronous methods (void methods whose first arguments are Return callbacks) for use with WrapperFunction::handle. WrapperFunction::handleWithSyncMethod can be used to wrap regular (non-asynchronous) methods for use with WrapperFunction::handle. Both variants return function objects that take a Return callback as their first argument, and an ExecutorAddr representing the address of the instance to call the object on. For asynchronous methods the resulting function object (AsyncMethod<method-ptr>) forwards the Return callback through to the method. For synchronous methods the method is called and the result passed to the Return callback.
1 parent 6b1604a commit 36cfdeb

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

orc-rt/include/orc-rt/WrapperFunction.h

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include "orc-rt-c/WrapperFunction.h"
1717
#include "orc-rt/CallableTraitsHelper.h"
1818
#include "orc-rt/Error.h"
19+
#include "orc-rt/ExecutorAddress.h"
1920
#include "orc-rt/bind.h"
21+
#include "orc-rt/move_only_function.h"
2022

2123
#include <utility>
2224

@@ -205,6 +207,128 @@ struct ResultDeserializer<std::tuple<Error>, Serializer> {
205207
/// wrapper functions in C++.
206208
struct WrapperFunction {
207209

210+
/// Wraps an asynchronous method (a method returning void, and taking a
211+
/// return callback as its first argument) for use with
212+
/// WrapperFunction::handle.
213+
///
214+
/// AsyncMethod's call operator takes an ExecutorAddr as its second argument,
215+
/// casts it to a ClassT*, and then calls the wrapped method on that pointer,
216+
/// forwarding the return callback and any subsequent arguments (after the
217+
/// second argument representing the object address).
218+
///
219+
/// This utility removes some of the boilerplate from writing wrappers for
220+
/// method calls.
221+
template <typename ClassT, typename ReturnT, typename... ArgTs>
222+
struct AsyncMethod {
223+
AsyncMethod(void (ClassT::*M)(ReturnT, ArgTs...)) : M(M) {}
224+
void operator()(ReturnT &&Return, ExecutorAddr Obj, ArgTs &&...Args) {
225+
(Obj.toPtr<ClassT *>()->*M)(std::forward<ReturnT>(Return),
226+
std::forward<ArgTs>(Args)...);
227+
}
228+
229+
private:
230+
void (ClassT::*M)(ReturnT, ArgTs...);
231+
};
232+
233+
/// Create an AsyncMethod wrapper for the given method pointer. The given
234+
/// method should be asynchronous: returning void, and taking a return
235+
/// callback as its first argument.
236+
///
237+
/// The handWithAsyncMethod function can be used to remove some of the
238+
/// boilerplate from writing wrappers for method calls:
239+
///
240+
/// @code{.cpp}
241+
/// class MyClass {
242+
/// public:
243+
/// void myMethod(move_only_function<void(std::string)> Return,
244+
// uint32_t X, bool Y) { ... }
245+
/// };
246+
///
247+
/// // SPS Method signature -- note MyClass object address as first
248+
/// // argument.
249+
/// using SPSMyMethodWrapperSignature =
250+
/// SPSString(SPSExecutorAddr, uint32_t, bool);
251+
///
252+
///
253+
/// static void adder_add_async_sps_wrapper(
254+
/// orc_rt_SessionRef Session, void *CallCtx,
255+
/// orc_rt_WrapperFunctionReturn Return,
256+
/// orc_rt_WrapperFunctionBuffer ArgBytes) {
257+
/// using SPSSig = SPSString(SPSExecutorAddr, int32_t, bool);
258+
/// SPSWrapperFunction<SPSSig>::handle(
259+
/// Session, CallCtx, Return, ArgBytes,
260+
/// WrapperFunction::handleWithAsyncMethod(&MyClass::myMethod));
261+
/// }
262+
/// @endcode
263+
///
264+
template <typename ClassT, typename ReturnT, typename... ArgTs>
265+
static AsyncMethod<ClassT, ReturnT, ArgTs...>
266+
handleWithAsyncMethod(void (ClassT::*M)(ReturnT, ArgTs...)) {
267+
return AsyncMethod<ClassT, ReturnT, ArgTs...>(M);
268+
}
269+
270+
/// Wraps a synchronous method (an ordinary method that returns its result,
271+
/// as opposed to an asynchronous method, see AsyncMethod) for use with
272+
/// WrapperFunction::handle.
273+
///
274+
/// SyncMethod's call operator takes a return callback as its first argument
275+
/// and an ExecutorAddr as its second argument. The ExecutorAddr argument is
276+
/// cast to a ClassT*, and then called passing the subsequent arguments
277+
/// (after the second argument representing the object address). The Return
278+
/// callback is then called on the value returned from the method.
279+
///
280+
/// This utility removes some of the boilerplate from writing wrappers for
281+
/// method calls.
282+
template <typename ClassT, typename RetT, typename... ArgTs>
283+
class SyncMethod {
284+
public:
285+
SyncMethod(RetT (ClassT::*M)(ArgTs...)) : M(M) {}
286+
287+
void operator()(move_only_function<void(RetT)> Return, ExecutorAddr Obj,
288+
ArgTs &&...Args) {
289+
Return((Obj.toPtr<ClassT *>()->*M)(std::forward<ArgTs>(Args)...));
290+
}
291+
292+
private:
293+
RetT (ClassT::*M)(ArgTs...);
294+
};
295+
296+
/// Create an SyncMethod wrapper for the given method pointer. The given
297+
/// method should be synchronous, i.e. returning its result (as opposed to
298+
/// asynchronous, see AsyncMethod).
299+
///
300+
/// The handWithAsyncMethod function can be used to remove some of the
301+
/// boilerplate from writing wrappers for method calls:
302+
///
303+
/// @code{.cpp}
304+
/// class MyClass {
305+
/// public:
306+
/// std::string myMethod(uint32_t X, bool Y) { ... }
307+
/// };
308+
///
309+
/// // SPS Method signature -- note MyClass object address as first
310+
/// // argument.
311+
/// using SPSMyMethodWrapperSignature =
312+
/// SPSString(SPSExecutorAddr, uint32_t, bool);
313+
///
314+
///
315+
/// static void adder_add_sync_sps_wrapper(
316+
/// orc_rt_SessionRef Session, void *CallCtx,
317+
/// orc_rt_WrapperFunctionReturn Return,
318+
/// orc_rt_WrapperFunctionBuffer ArgBytes) {
319+
/// using SPSSig = SPSString(SPSExecutorAddr, int32_t, bool);
320+
/// SPSWrapperFunction<SPSSig>::handle(
321+
/// Session, CallCtx, Return, ArgBytes,
322+
/// WrapperFunction::handleWithSyncMethod(&Adder::addSync));
323+
/// }
324+
/// @endcode
325+
///
326+
template <typename ClassT, typename RetT, typename... ArgTs>
327+
static SyncMethod<ClassT, RetT, ArgTs...>
328+
handleWithSyncMethod(RetT (ClassT::*M)(ArgTs...)) {
329+
return SyncMethod<ClassT, RetT, ArgTs...>(M);
330+
}
331+
208332
/// Make a call to a wrapper function.
209333
///
210334
/// This utility serializes and deserializes arguments and return values

orc-rt/unittests/SPSWrapperFunctionTest.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,53 @@ TEST(SPSWrapperFunctionUtilsTest, TestHandlerWithReferences) {
297297
EXPECT_EQ(OpCounter<3>::moves(), 1U);
298298
EXPECT_EQ(OpCounter<3>::copies(), 0U);
299299
}
300+
301+
namespace {
302+
class Adder {
303+
public:
304+
int32_t addSync(int32_t X, int32_t Y) { return X + Y; }
305+
void addAsync(move_only_function<void(int32_t)> Return, int32_t X,
306+
int32_t Y) {
307+
Return(addSync(X, Y));
308+
}
309+
};
310+
} // anonymous namespace
311+
312+
static void adder_add_async_sps_wrapper(orc_rt_SessionRef Session,
313+
void *CallCtx,
314+
orc_rt_WrapperFunctionReturn Return,
315+
orc_rt_WrapperFunctionBuffer ArgBytes) {
316+
SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::handle(
317+
Session, CallCtx, Return, ArgBytes,
318+
WrapperFunction::handleWithAsyncMethod(&Adder::addAsync));
319+
}
320+
321+
TEST(SPSWrapperFunctionUtilsTest, HandleWtihAsyncMethod) {
322+
auto A = std::make_unique<Adder>();
323+
int32_t Result = 0;
324+
SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::call(
325+
DirectCaller(nullptr, adder_add_async_sps_wrapper),
326+
[&](Expected<int32_t> R) { Result = cantFail(std::move(R)); },
327+
ExecutorAddr::fromPtr(A.get()), 41, 1);
328+
329+
EXPECT_EQ(Result, 42);
330+
}
331+
332+
static void adder_add_sync_sps_wrapper(orc_rt_SessionRef Session, void *CallCtx,
333+
orc_rt_WrapperFunctionReturn Return,
334+
orc_rt_WrapperFunctionBuffer ArgBytes) {
335+
SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::handle(
336+
Session, CallCtx, Return, ArgBytes,
337+
WrapperFunction::handleWithSyncMethod(&Adder::addSync));
338+
}
339+
340+
TEST(SPSWrapperFunctionUtilsTest, HandleWithSyncMethod) {
341+
auto A = std::make_unique<Adder>();
342+
int32_t Result = 0;
343+
SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::call(
344+
DirectCaller(nullptr, adder_add_sync_sps_wrapper),
345+
[&](Expected<int32_t> R) { Result = cantFail(std::move(R)); },
346+
ExecutorAddr::fromPtr(A.get()), 41, 1);
347+
348+
EXPECT_EQ(Result, 42);
349+
}

0 commit comments

Comments
 (0)