Skip to content
This repository has been archived by the owner on Mar 26, 2020. It is now read-only.

Proxy interface generation #337

Open
csimmons0 opened this issue Nov 14, 2017 · 4 comments
Open

Proxy interface generation #337

csimmons0 opened this issue Nov 14, 2017 · 4 comments
Assignees

Comments

@csimmons0
Copy link
Contributor

In order to break reference cycles between view and view model classes, I regularly create a proxy view class that holds a weak reference to the actual view, as described by mknejp in #110. I'm working on a change to generate that proxy class automatically. The change is complete aside from the logic to determine which interfaces Djinni generates a proxy for. I have a couple of questions:

  1. Is this a change that you would consider merging?
  2. What might be a good way to indicate which interfaces Djinni should generate proxies for? If the proxies were for records, I'd add a new "deriving" option, but the proxies are for interfaces. When I tried to add a "+w" option to go alongside "+c", "+o", and "+j", I couldn't figure out how to make the IDL parser accept "+w". It would see "+w" in an interface declaration and abort with the error, "+' expected but {' found." That said, I wasn't crazy about using "+w" to indicate that I wanted a proxy anyways.

This is what I have so far: csimmons0/djinni@develop...csimmons0:weak-proxy

@artwyman
Copy link
Contributor

I don't think I understand what you're proposing to generate well enough to have a clear opinion yet. A "proxy interface" can mean a lot of things, and indeed both of those words have existing meanings in Djinni which I'd suggest you not overload. My thoughts on your questions at a high level:

  1. I think my thoughts on mergeability would depend primarily on two things:
  • How general a pattern is this providing? Would this be useful in a lot of use cases or is it specific to a particular kind of view/view-controller architecture? In general, I think Djinni should provide general building-blocks, not push people toward specific solutions.
  • How hard is this to implement by hand? Can this be accomplished with a recommended pattern and some sample code? Is the generator providing code which is error-prone or tedious to write manually?
  1. I think something like a deriving would be a good way to represent this. Currently there isn't any deriving clause on interfaces, but it should be feasible to add. I think the +c style annotations are hard to understand as-is, so I wouldn't add more concepts to them beyond the "where can this interface be implemented" they cover today. The Scala parser is kinda magical and hard to read even for me, but I think that's the thing you'd have to figure out in order to add either of these as a new option.

@csimmons0
Copy link
Contributor Author

"Proxy interface" was a poor choice of words on my part. Allow me to explain. For each page in my mobile app, there is typically a Djinni interface for the ViewController/Activity and a Djinni interface for the C++ view model. The ViewController/Activity that implements the one interface makes calls to the view model that implements the other interface and vice-versa. The view model's interface includes a static "create" function, and it is the ViewController/Activity that creates an instance of the view model and has ownership of it.

Where possible, I make it so that the view model doesn't keep a permanent reference to the view. The view model's methods just take the view as an argument, the way they would if the view model were written in a purely functional language. Doing things that way is not always easy; sometimes it makes more sense to give the view model a permanent reference to the ViewController/Activity. In that case, I have to place a proxy implementation of the view's interface in between the view model and the ViewController/Activity in order to break a reference cycle, as the ViewController/Activity owns the view model.

This is an abbreviated example of one of my view interfaces and its Java proxy implementation.

messaging_display = interface +o +j {
    set_chat_title(title: string);
}
private static class MessagingDisplayProxy implements MessagingDisplay
{
    private WeakReference<MessagingDisplay> display;

    MessagingDisplayProxy(MessagingDisplay display)
    {
        this.display = new WeakReference<MessagingDisplay>(display);
    }

    public void setChatTitle(String content)
    {
        MessagingDisplay messagingDisplay = display.get();
        if (messagingDisplay != null)
        {
            messagingDisplay.setChatTitle(content);
        }
    }
}

The class above and its ObjC counterpart are what I want to generate. It's certainly not hard to implement, but it's violating the DRY principle to implement it by hand, and it's a real bother to have to write or update this boilerplate code in two languages every time I create or edit a Djinni interface that has proxy implementations. I'm not sure how useful this is to others, although mknejp said a while back that he did things this way. If I had all the time in the world, I think that I would add to Djinni generic support for plugins, and I would create a separate plugin to generate this bit of code. I don't think I'll have time for that though.

@artwyman
Copy link
Contributor

Interesting. So it sounds like your use case for this "proxy" is specific to a particular MVVM pattern, but what you need Djinni to generate isn't. If I'm understanding it correctly, the general pattern is for interface X, you want to generate a concrete class Y which implements the interface X, and holds a weak reference to another X to which it forwards all methods. It sounds like you'd want Djinni to generate Y as a concrete class in the same languages as X can be implemented (so Java and ObjC in the case of your +j +o class above).

That seems like a generic enough feature to fit in Djinni. The plug-in suggestion you brought up would be even better as a general mechanism, but I could see this feature as a built-in option. As described, I think deriving is the right place to put it, and I'd call it weak-wrapper, weak-forwarder or something similar like that? I'd suggest avoiding the "proxy" terminology since that's already used for the cross-language proxy objects, and your proposed implementation here would build on top of that, rather than being a replacement for that.

There might also be an approach to this which would be built into the proxy layer, perhaps operating off of strong/weak annotations on args/returns in the IDL. Even better would be finding some way to make plain weak_ptr work across languages. I think either of those are probably too much of a stretch unless someone is excited and has an idea of how to accomplish them.

@csimmons0
Copy link
Contributor Author

I'll try to get around to this over the holiday break.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants