Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Callable Objects in Java, C#, Rust, and C++

Callable Objects in Java, C#, Rust, and C++

It is trivial to make a class callable in C++. Just define operator(). But what if you cannot? My ACCU 2023 online talk.

Zhihao Yuan

April 22, 2023
Tweet

More Decks by Zhihao Yuan

Other Decks in Programming

Transcript

  1. class MyRunnable implements Runnable { public void run() { System.out.println("MyRunnable

    running"); } } MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start();
  2. class lambda$1 implements Runnable { public void run() { System.out.println("MyRunnable

    running"); } } Thread thread = new Thread(new lambda$1());
  3. public interface Function<T,R> { R apply(T t); } class ZipFile

    { byte[] read(int) { } } public class Tab { static Tab take(Function<String,byte[]> g);
  4. public interface Function<T,R> { R apply(T t); } class ZipFile

    { byte[] read(int) { } } ZipFile zipped = new ZipFile("input.zip"); Tab::take(/* ?? */);
  5. public interface Function<T,R> { R apply(T t); } class ZipFile

    { byte[] read(int) { } } ZipFile zipped = new ZipFile("input.zip"); Tab::take(zipped::read);
  6. class lambda$1 implements Function<String,byte[]> { final ZipFile arg0; byte[] apply(String

    t) { return arg0.read(t); } } ZipFile zipped = new ZipFile("input.zip"); Tab::take(new lambda$1(zipped));
  7. class Foo { public void test() { Thread thread =

    new Thread(() -> { System.out.println("MyRunnable running"); }); thread.start(); } }
  8. class Foo { private static void lambda$test$0() { System.out.println("MyRunnable running");

    } public void test() { Thread thread = new Thread( /* a callsite object to invoke lambda$test$0 */ ); thread.start(); } }
  9. ♯ • public interface Runnable { void run(); } •

    ♯ public delegate void Runnable();
  10. ♯ • public interface Runnable { void run(); } •

    ♯ public delegate void ThreadStart();
  11. class MyRunnable implements Runnable { public void run() { System.out.println("MyRunnable

    running"); } } MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start();
  12. class MyRunnable { public void run() { Console.WriteLine("MyRunnable running"); }

    } MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable.run); thread.Start();
  13. internal class Delegate__ThreadStart { public virtual void Invoke(); } internal

    class Lambda__1 : Delegate__ThreadStart { readonly MyRunnable arg0; public override void Invoke() { arg0.run(); } }
  14. internal class Delegate__ThreadStart : System.Delegate { public virtual void Invoke();

    } namespace System { public abstract partial class Delegate { // null if static delegate internal object? _target; // _methodPtr could point to a thunk internal IntPtr _methodPtr;
  15. namespace System { public abstract partial class Delegate { //

    null if static delegate internal object? _target; // _methodPtr could point to a thunk internal IntPtr _methodPtr;
  16. namespace std { template <typename Ret, typename... Params> class function_ref<Ret(Params...)>

    { // null if free function void *_target = nullptr; // _methodPtr points to a thunk Ret (*_methodPtr)(void *, Params...);
  17. struct MyReportingCallback { name: String, // more fields } impl

    MyReportingCallback { // member functions go here }
  18. impl MyReportingCallback { fn notify(&self, status: WorkResult, ref obj: Data)

    {} } fn accept<T: Callable<(WorkResult, Data)>>(cb: &T) { let w = WorkResult {}; let o = Data {}; cb.call((w, o)); }
  19. impl MyReportingCallback { fn notify(&self, status: WorkResult, ref obj: Data)

    {} } impl Callable<(WorkResult, Data)> for MyReportingCallback { fn call(&self, (status, object): (WorkResult, Data)) { self.notify(status, object); } }
  20. • trait Callable<Args> { fn call(&self, args: Args); } •

    template<class Fn, class... T> concept invocable = std::is_invocable_v<Fn, T...>;
  21. trait Callable<Args> { fn call(&self, args: Args); } impl Callable<(WorkResult,

    Data)> for MyReportingCallback { fn call(&self, (status, object): (WorkResult, Data)) { self.notify(status, object); } }
  22. concept invocable<class Fn, class... T> { /*...*/ }; concept_map invocable<MyReportingCallback,

    WorkResult, Data&> { using operator() = MyReportingCallback::notify; };
  23. pub trait FnOnce<Args> where Args: Tuple, { type Output; extern

    "rust-call" fn call_once(self, args: Args) -> Self::Output; } // std::thread pub fn spawn<F, T>(f: F) -> JoinHandle<T> where F: FnOnce() -> T
  24. class CDraw : public IDraw { Color color_; public: void

    draw() override; void set_color(Color) override; ~CDraw() = default; }; class IDraw { public: virtual void draw() = 0; virtual void set_color(Color) = 0; ~IDraw() = default; };
  25. constinit Drawable DrawableImpl = { .draw = [](void *this_) {},

    .set_color = [](void *this_, Color color) {}, .destroy = [](void *this_) {}, }; struct Drawable { typedef void draw_t(void *); typedef void set_color_t(void *, Color); typedef void destroy_t(void *); draw_t *draw; set_color_t *set_color; destroy_t *destroy; };
  26. DrawableImpl static object void (*draw)(void *); void (*set_color)(void *, Color);

    void (*destroy)(void *); ref-wrap<Drawable const> CDraw rep_; ref-wrap<Drawable const> void *obj_;
  27. DrawableImpl static object void (*draw)(void *); void (*set_color)(void *, Color);

    void (*destroy)(void *); Drawable const & unique_ptr<void> rep_; Drawable const & void *obj_;
  28. constinit Drawable DrawableImpl = { .draw = [](void *this_) {},

    .set_color = [](void *this_, Color color) {}, .destroy = [](void *this_) {}, }; struct Drawable { typedef void draw_t(void *); typedef void set_color_t(void *, Color); typedef void destroy_t(void *); draw_t *draw; set_color_t *set_color; destroy_t *destroy; };
  29. impl Drawable for MyType { fn draw() { } fn

    set_color(color: Color) { } fn destroy() { } } trait Drawable { fn draw(); fn set_color(_: Color); fn destroy(); }
  30. function_ref<R(Args...)> function_ref<R(Args...) const> move_only_function<R(Args...)> move_only_function<R(Args...) const> copyable_function<R(Args...)> copyable_function<R(Args...) const> &dyn

    FnMut<(Args) -> R> &dyn Fn<(Args) -> R> Box<dyn FnMut<(Args) -> R>> Box<dyn Fn<(Args) -> R>> Box<dyn Clone + FnMut<(Args) -> R>> Box<dyn Clone + Fn<(Args) -> R>>
  31. struct MyReportingCallback { void notify(WorkResult status, Data& object); }; void

    accept(std::function_ref<void(WorkResult, Data&)> cb);
  32. MyReportingCallback cb; accept({std::nontype< [](auto &cb, WorkResult status, Data &object) {

    LOG(INFO) << "status: " << status; cb.notify(status, object); }>, cb});
  33. constinit Drawable DrawableImpl = { .draw = [](void *this_) {},

    .set_color = [](void *this_, Color color) {}, .destroy = [](void *this_) {}, };
  34. constinit Drawable DrawableImpl = { .draw = [](void *this_) {},

    .set_color = [](void *this_, Color color) {}, .destroy = [](void *this_) {}, };
  35. MyReportingCallback cb; accept({std::nontype< [](auto &cb, WorkResult status, Data &object) {

    LOG(INFO) << "status: " << status; cb.notify(status, object); }>, cb});
  36. template<class T> inline constexpr auto impl_invocable_for = std::nontype<void>; template<> inline

    constexpr auto impl_invocable_for<MyReportingCallback> = std::nontype< [](auto &cb, WorkResult status, Data &object) { LOG(INFO) << "status: " << status; cb.notify(status, object); }>; MyReportingCallback cb; accept({impl_invocable_for<MyReportingCallback>, cb});