Simple example of Builder to Reader creation

608 views
Skip to first unread message

Steve Lorimer

unread,
Mar 29, 2015, 3:33:06 AM3/29/15
to capn...@googlegroups.com
In an attempt to teach myself capnp I created a simple example app which created a person Builder, writes first-name/last-name, and then created a Reader from the Builder and prints out the result

Here is my schema:

@0x9619d46261603f52;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("msg");

struct Person 
{
    firstName @0 :Text;
    lastName  @1 :Text;
}

Here is my test app:

#include <iostream>
#include "test.capnp.h"
#include <capnp/message.h>

msg::Person::Builder write()
{
    capnp::MallocMessageBuilder message;

    msg::Person::Builder person = message.initRoot<msg::Person>();
    person.setFirstName("Bob");
    person.setLastName("Builder");

    return person;
}

void print(const msg::Person::Reader& person)
{
    std::cout << "person: " << person.getFirstName().cStr() << ' ' << person.getLastName().cStr() << '\n';
}

int main()
{
    print(write());
}

The source has a Reader operator, which suggests it is possible to create a Reader from a Builder - is that the true?
I get a core dump when I run the app - what am I doing wrong please?

Also, what is the cost of creating a Reader from a Builder? I have the use case where I want to provide a consistent interface, but sometimes the consumer may be in-process (hence create a Reader from the Builder) and other times distributed (serialise and write to fd) Is that a valid use case?

Valgrind output below


==16101== Invalid read of size 4
==16101==    at 0x411FE3: capnp::Text::Reader capnp::_::PointerReader::getBlob<capnp::Text>(void const*, unsigned int) const (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40E04A: capnp::_::PointerHelpers<capnp::Text, (capnp::Kind)1>::get(capnp::_::PointerReader, void const*, unsigned int) (pointer-helpers.h:110)
==16101==    by 0x40DFC6: msg::Person::Reader::getLastName() const (test.capnp.h:174)
==16101==    by 0x40DA14: print(msg::Person::Reader const&) (msg_test.cpp:18)
==16101==    by 0x40DAC9: main (msg_test.cpp:23)
==16101==  Address 0x5c3a050 is 16 bytes inside a block of size 8,192 free'd
==16101==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16101==    by 0x41EF6B: capnp::MallocMessageBuilder::~MallocMessageBuilder() (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40D9C9: write() (msg_test.cpp:13)
==16101==    by 0x40DAAA: main (msg_test.cpp:23)
==16101== 
==16101== Invalid read of size 4
==16101==    at 0x411FE5: capnp::Text::Reader capnp::_::PointerReader::getBlob<capnp::Text>(void const*, unsigned int) const (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40E04A: capnp::_::PointerHelpers<capnp::Text, (capnp::Kind)1>::get(capnp::_::PointerReader, void const*, unsigned int) (pointer-helpers.h:110)
==16101==    by 0x40DFC6: msg::Person::Reader::getLastName() const (test.capnp.h:174)
==16101==    by 0x40DA14: print(msg::Person::Reader const&) (msg_test.cpp:18)
==16101==    by 0x40DAC9: main (msg_test.cpp:23)
==16101==  Address 0x5c3a054 is 20 bytes inside a block of size 8,192 free'd
==16101==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16101==    by 0x41EF6B: capnp::MallocMessageBuilder::~MallocMessageBuilder() (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40D9C9: write() (msg_test.cpp:13)
==16101==    by 0x40DAAA: main (msg_test.cpp:23)
==16101== 
==16101== Invalid read of size 4
==16101==    at 0x412036: capnp::Text::Reader capnp::_::PointerReader::getBlob<capnp::Text>(void const*, unsigned int) const (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40E04A: capnp::_::PointerHelpers<capnp::Text, (capnp::Kind)1>::get(capnp::_::PointerReader, void const*, unsigned int) (pointer-helpers.h:110)
==16101==    by 0x40DFC6: msg::Person::Reader::getLastName() const (test.capnp.h:174)
==16101==    by 0x40DA14: print(msg::Person::Reader const&) (msg_test.cpp:18)
==16101==    by 0x40DAC9: main (msg_test.cpp:23)
==16101==  Address 0x5c3a054 is 20 bytes inside a block of size 8,192 free'd
==16101==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16101==    by 0x41EF6B: capnp::MallocMessageBuilder::~MallocMessageBuilder() (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40D9C9: write() (msg_test.cpp:13)
==16101==    by 0x40DAAA: main (msg_test.cpp:23)
==16101== 
==16101== Invalid read of size 4
==16101==    at 0x412039: capnp::Text::Reader capnp::_::PointerReader::getBlob<capnp::Text>(void const*, unsigned int) const (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40E04A: capnp::_::PointerHelpers<capnp::Text, (capnp::Kind)1>::get(capnp::_::PointerReader, void const*, unsigned int) (pointer-helpers.h:110)
==16101==    by 0x40DFC6: msg::Person::Reader::getLastName() const (test.capnp.h:174)
==16101==    by 0x40DA14: print(msg::Person::Reader const&) (msg_test.cpp:18)
==16101==    by 0x40DAC9: main (msg_test.cpp:23)
==16101==  Address 0x5c3a050 is 16 bytes inside a block of size 8,192 free'd
==16101==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16101==    by 0x41EF6B: capnp::MallocMessageBuilder::~MallocMessageBuilder() (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40D9C9: write() (msg_test.cpp:13)
==16101==    by 0x40DAAA: main (msg_test.cpp:23)
==16101== 
terminate called after throwing an instance of 'kj::ExceptionImpl'
  what():  src/capnp/layout.c++:2116: failed: expected boundsCheck(segment, ptr, ptr + roundBytesUpToWords(ref->listRef.elementCount() * (1 * BYTES / ELEMENTS))); Message contained out-of-bounds text pointer.
stack: 0x422b39 0x42629a 0x41b337 0x412095 0x40e04b 0x40dfc7 0x40da15 0x40daca 0x5590ec5 0x40d849
==16101== 163 bytes in 1 blocks are possibly lost in loss record 1 of 3
==16101==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16101==    by 0x421F83: kj::_::HeapArrayDisposer::allocateImpl(unsigned long, unsigned long, unsigned long, void (*)(void*), void (*)(void*)) (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x422182: kj::heapString(unsigned long) (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x425C80: kj::_::(anonymous namespace)::makeDescriptionImpl(kj::_::(anonymous namespace)::DescriptionStyle, char const*, int, char const*, kj::ArrayPtr<kj::String>) (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x426276: kj::_::Debug::Fault::init(char const*, int, kj::Exception::Type, char const*, char const*, kj::ArrayPtr<kj::String>) (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x41B336: kj::_::Debug::Fault::Fault<char const (&) [46]>(char const*, int, kj::Exception::Type, char const*, char const*, char const (&) [46]) (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x412094: capnp::Text::Reader capnp::_::PointerReader::getBlob<capnp::Text>(void const*, unsigned int) const (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40E04A: capnp::_::PointerHelpers<capnp::Text, (capnp::Kind)1>::get(capnp::_::PointerReader, void const*, unsigned int) (pointer-helpers.h:110)
==16101==    by 0x40DFC6: msg::Person::Reader::getLastName() const (test.capnp.h:174)
==16101==    by 0x40DA14: print(msg::Person::Reader const&) (msg_test.cpp:18)
==16101==    by 0x40DAC9: main (msg_test.cpp:23)
==16101== 
==16101== 376 bytes in 1 blocks are possibly lost in loss record 3 of 3
==16101==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16101==    by 0x50B24E2: __cxa_allocate_exception (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==16101==    by 0x424FAB: kj::ExceptionCallback::RootExceptionCallback::onRecoverableException(kj::Exception&&) (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x425248: kj::_::Debug::Fault::~Fault() (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x41209E: capnp::Text::Reader capnp::_::PointerReader::getBlob<capnp::Text>(void const*, unsigned int) const (in /home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test)
==16101==    by 0x40E04A: capnp::_::PointerHelpers<capnp::Text, (capnp::Kind)1>::get(capnp::_::PointerReader, void const*, unsigned int) (pointer-helpers.h:110)
==16101==    by 0x40DFC6: msg::Person::Reader::getLastName() const (test.capnp.h:174)
==16101==    by 0x40DA14: print(msg::Person::Reader const&) (msg_test.cpp:18)
==16101==    by 0x40DAC9: main (msg_test.cpp:23)
==16101== 
Aborted (core dumped)
make: *** [/home/steve/.build/lori/x86_64/2.19/gcc-4.8.2/debug/test/msg_test.passed] Error 134

Farz Hem

unread,
Mar 29, 2015, 5:08:55 AM3/29/15
to Steve Lorimer, capnproto

You can't use the person object like that, it's being destructed in write. Store it then pass it to print:

auto p = write();
print(p);

David Renshaw

unread,
Mar 29, 2015, 9:07:48 AM3/29/15
to Steve Lorimer, capnproto
On Sun, Mar 29, 2015 at 3:33 AM, Steve Lorimer <steve....@gmail.com> wrote:

The source has a Reader operator, which suggests it is possible to create a Reader from a Builder - is that the true?
I get a core dump when I run the app - what am I doing wrong please?

Your `write()` function returns a dangling reference.  A `msg::Person::Builder` is only valid for as long as the `MesssageBuilder` that contains it is valid. In your case, the `MessageBuilder` only lives for the scope of the `write()` function, but the `msg::Person::Builder` escapes that scope. Here is a way you could rewrite your program to avoid such problems:


```
#include <iostream>
#include "test.capnp.h"
#include <capnp/message.h>

msg::Person::Builder write(msg::Person::Builder person)
{
    person.setFirstName("Bob");
    person.setLastName("Builder");

    return person;
}

void print(const msg::Person::Reader person)
{
    std::cout << "person: " << person.getFirstName().cStr() << ' ' << person.getLastName().cStr() << '\n';
}

int main()
{
  capnp::MallocMessageBuilder message;
  print(write(message.initRoot<msg::Person>()));
}

```



Also, what is the cost of creating a Reader from a Builder? I have the use case where I want to provide a consistent interface, but sometimes the consumer may be in-process (hence create a Reader from the Builder) and other times distributed (serialise and write to fd) Is that a valid use case?


Creating a Reader from a Builder is very cheap, as the Builder is reinterpreted in-place without performing any allocations. And yes, the use case you describe is a valid use case.


- David



Steve Lorimer

unread,
Mar 29, 2015, 4:58:00 PM3/29/15
to capn...@googlegroups.com, steve....@gmail.com, da...@sandstorm.io
Thanks for the input. I have successfully printed a person! :)

Now I want to embed a struct inside another.

Taking my example code a step further, I allocate a Person, and set that person into a Customer.

However, when I try access the person, it is now null.

My schema:

@0x9619d46261603f52;

using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("msg");

struct Person 
{
    firstName @0 :Text;
    lastName  @1 :Text;
}

struct Customer
{
    person    @0 :Person;
}

My code:

#include <iostream>
#include "test.capnp.h"
#include <capnp/message.h>

capnp::MallocMessageBuilder message;

msg::Customer::Builder write()
{
    msg::Person::Builder person = message.initRoot<msg::Person>();
    person.setFirstName("Bob");
    person.setLastName("Builder");

    msg::Customer::Builder customer = message.initRoot<msg::Customer>();
    customer.setPerson(person);
    return customer;
}

void print(const msg::Customer::Reader& customer)
{
    std::cout << "person: " << customer.getPerson().getFirstName().cStr() << ' ' << customer.getPerson().getLastName().cStr() << '\n';
}

int main()
{
    print(write());
}


How can I achieve what I'm trying to do here please?

TIA
Steve

David Renshaw

unread,
Mar 29, 2015, 5:12:07 PM3/29/15
to Steve Lorimer, capnproto
On Sun, Mar 29, 2015 at 4:58 PM, Steve Lorimer <steve....@gmail.com> wrote:

How can I achieve what I'm trying to do here please?




You should only call `initRoot()` once. After you call `initRoot()` the second time, your `person` variable points to zeroed memory.

Try this instead:


```
#include <iostream>
#include "test.capnp.h"
#include <capnp/message.h>

capnp::MallocMessageBuilder message;

msg::Customer::Builder write()
{
    msg::Customer::Builder customer = message.initRoot<msg::Customer>();
    auto person = customer.initPerson();
    person.setFirstName("Bob");
    person.setLastName("Builder");
    return customer;
}

void print(const msg::Customer::Reader customer)
{
    std::cout << "person: " << customer.getPerson().getFirstName().cStr() << ' ' << customer.getPerson().getLastName().cStr() << '\n';
}

int main()
{
    print(write());
}

``` 


- David

Steve Lorimer

unread,
Mar 29, 2015, 5:21:34 PM3/29/15
to David Renshaw, capnproto

Thanks, I'm an idiot, I should RTFM! >.<

Reply all
Reply to author
Forward
0 new messages