Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated code format #29

Open
andrewmellinger opened this issue Apr 16, 2015 · 2 comments
Open

Generated code format #29

andrewmellinger opened this issue Apr 16, 2015 · 2 comments

Comments

@andrewmellinger
Copy link

Greetings!

Thanks so much for taking this project on an all the work.

While experimenting with this I was wondering about the output format and the reasons for it. It seems that since you are generating the classes you could wrap some of the boilerplate in the generated code. For example, a cleaner end-user interface might be something like:

// Under the hood this does new MessageBuiilder and initRoot
AddressBook addressBook = new AddressBook();

// The People inner class wraps the generics
AddressBook.People people = addressBook.initPeople(2);

// Again, we wrap the generics
AddressBook.Person alice = people.get(0);
alice.setId(123);
alice.setName("Alice");
alice.setEmail("[email protected]");

AddressBook.Person.Phones alicePhones = alice.initPhones(1)
alicePhones.get(0).setNumber("555-1212");

...etc.

This would get rid of a lot of the messy generic boilerplate. Offhand, do you know of anything that would prevent it from being modified (i.e. by me) to do this?

Thanks,
-Andrew

@dwrensha
Copy link
Member

Hi!

Looks like there a several components to what you're proposing. The first idea is to bypass the need to explicitly create a MessageBuilder by allowing this pattern instead:

AddressBook.Builder addressBook = new AddressBook.Builder();

I think this might make a lot of sense! The primary purpose of MessageBuilder in the C++ implementation is to manage the memory of the backing buffers of a message. In Java, we can rely on the garbage collector. Open questions: How would we still provide access to important methods like MessageBuilder.getSegmentsForOutput()? Maybe AddressBook.Builder would be a subclass of MessageBuilder? One useful feature of MessageBuilder in capnproto-c++ is that it allows users to provide their own segment allocator through the virtual allocateSegment() method. Would we still be able to provide that kind of flexibility? Are Java users even going to care if we don't provide that kind of flexibility?

Another idea in your proposal, if I understand correctly, is to combine AddressBook.Builder and AddressBook.Reader into a single class AddressBook. I don't think that would work, for the simple reason that these two classes have different methods. E.g. the builder has setter methods and the reader does not. It might be possible to make AddressBook.Builder a subclass of AddressBook.Reader, but then what would AddressBook.Builder.getPeople() return? I guess it would be a StructList.Reader<Person.Reader> that you're allowed to downcast to a StructList.Builder<Person.Builder>? That's ugly. So maybe instead there would be separate methods:

StructList.Reader<Person.Reader> AddressBook.Reader.getPeople();

and

StructList.Builder<Person.Builder> AddressBook.Builder.getPeople();

and we would need to come up with a new name for one of them, maybe readPeople(). My sense is that this would not be worth the trouble.

A third idea in your proposal is to include something like type aliases in the generated code. I guess the way this would work is that there would be generated code that looks like this:

class Phones {
  class Builder extends org.capnproto.StructList.Builder<PhoneNumber.Builder> {}
  class Reader extends org.capnproto.StructList.Reader<PhoneNumber.Reader> {}
}

I suppose this might provide some convenience. You would need to decide how to deal with the case when this would cause a name collision, for example if there was already a Person.Phones struct defined in addressbook.capnp.

@andrewmellinger
Copy link
Author

David,

Thanks for the analysis! What I am gleaning at a high level is that there is some value in massaging the generator, but that it won't be a quick hours work. Let me see if I can provide some useful thoughts on your feedback.

Regarding the embedding of MessageBuilder in the AddressBook. Since it seems like it is useful, but not always needed, would it make sense to do addressBook.getMessageBuilder()? The use could still get to it if they needed it, but wouldn't be the first thing they see.

On the case of blending the Reader and Builder interface, yes I was myopically focused on the reader. You're right, probably having something like new AddressBook.Reader(...) would be the simplest way to go. I think there might be some gains to also have custom inner classes for each of the various lists. So PersonReaderList or something like that.

You're probably wondering on my purpose. I am working on a piece of middleware and I want to allow external plugin developers to make their own messages. We'll provide a set of base message types in capnp, so they be constrained to use it. I expect these users to be academics, and I want to minimize the amount of learning so they can spend more time on their algorithms. Ideally, they would have a very standard bean like interface.

Again at this point it sounds like I need to try modifying the code to see what works and what doesn't. Any other pointers to what would probably not work would be highly appreciated.

Thanks!
-Andrew

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

No branches or pull requests

2 participants