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

API is not very ergonomic #151

Closed
Timmmm opened this issue Oct 11, 2019 · 3 comments
Closed

API is not very ergonomic #151

Timmmm opened this issue Oct 11, 2019 · 3 comments

Comments

@Timmmm
Copy link
Contributor

Timmmm commented Oct 11, 2019

Compare:

Prost

Generated Code

#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Memory {
    #[prost(uint32, repeated, tag="1")]
    pub memory: ::std::vec::Vec<u32>,
}

Decode

use prost::Message;

let mem: Message = Message::decode(<buffer>)?;

Capnp

Generated Code

pub mod variable_allocator_info {
  #[derive(Copy, Clone)]
  pub struct Owned;
  impl <'a> ::capnp::traits::Owned<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
  impl <'a> ::capnp::traits::OwnedStruct<'a> for Owned { type Reader = Reader<'a>; type Builder = Builder<'a>; }
  impl ::capnp::traits::Pipelined for Owned { type Pipeline = Pipeline; }

  #[derive(Clone, Copy)]
  pub struct Reader<'a> { reader: ::capnp::private::layout::StructReader<'a> }

  impl <'a,> ::capnp::traits::HasTypeId for Reader<'a,>  {
    #[inline]
    fn type_id() -> u64 { _private::TYPE_ID }
  }
  impl <'a,> ::capnp::traits::FromStructReader<'a> for Reader<'a,>  {
    fn new(reader: ::capnp::private::layout::StructReader<'a>) -> Reader<'a,> {
      Reader { reader: reader,  }
    }
  }

  impl <'a,> ::capnp::traits::FromPointerReader<'a> for Reader<'a,>  {
    fn get_from_pointer(reader: &::capnp::private::layout::PointerReader<'a>, default: ::std::option::Option<&'a [::capnp::Word]>) -> ::capnp::Result<Reader<'a,>> {
      ::std::result::Result::Ok(::capnp::traits::FromStructReader::new(reader.get_struct(default)?))
    }
  }

  impl <'a,> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a,>  {
    fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
      self.reader
    }
  }

  impl <'a,> ::capnp::traits::Imbue<'a> for Reader<'a,>  {
    fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
      self.reader.imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
    }
  }

  impl <'a,> Reader<'a,>  {
    pub fn reborrow(&self) -> Reader<> {
      Reader { .. *self }
    }

    pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
      self.reader.total_size()
    }
    #[inline]
    pub fn get_target_memory_info(self) -> ::capnp::Result<crate::VariableAllocator_capnp::target_memory_info::Reader<'a>> {
      ::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(0), ::std::option::Option::None)
    }
    pub fn has_target_memory_info(&self) -> bool {
      !self.reader.get_pointer_field(0).is_null()
    }
    #[inline]
    pub fn get_vars(self) -> ::capnp::Result<::capnp::struct_list::Reader<'a,crate::VariableAllocator_capnp::variable_allocator_info::var_info_entry::Owned>> {
      ::capnp::traits::FromPointerReader::get_from_pointer(&self.reader.get_pointer_field(1), ::std::option::Option::None)
    }

Decode

I don't actually know how to do the equivalent of Prost's decode() - i.e. read everything into a nice struct in memory. In C++ land we basically do it all manually, which is a huge pain.

It would be a lot nicer if this could just generate normal structs with a #[derive()] macro. I assume you didn't do this because it gives up on the zero copy stuff, but frankly I doubt many people would be willing to go to this level of API awkwardness for a very small performance increase.

@Timmmm
Copy link
Contributor Author

Timmmm commented Oct 11, 2019

Also unless I am missing something, if you do want to load everything into memory, the fact that Reader holds a reference to its std::io::Reader makes it really awkward. I think you basically have to use the rental crate which seems very complicated. As far as I can tell the only other options are:

  1. Store the data as a Vec<u8> and make a new Reader every time you want some data.
  2. Manually do what Prost does - create native Rust structs for the capnp types, and then tediously write code to fill them. I've opted for this because I don't have too many types but it would be really nice if there were a way to do it automatically like Prost does.

@dwrensha
Copy link
Member

Thanks for the feedback!

There was discussion about mapping into Rust-native structs over on issue #136.

Have you looked at message::TypedReader? In many situations that should allow you to pass around a capnp::Message without worrying about lifetimes.

@Timmmm
Copy link
Contributor Author

Timmmm commented Oct 22, 2019

Ah yes TypeReader helps. You might want to add some documentation for it! I think I will still stick with manually decoding into structs though because the Reader API is so awkward.

#136 looks promising though! I'll close this because it's essentially the same.

@Timmmm Timmmm closed this as completed Oct 22, 2019
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