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

CPlusPlusGenerator: Avoid unnecessary copy by using a const ref to the input vector #356

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

maddouri
Copy link

The current version of bebopc generates, among others, decoding methods that take in an input byte vector by copy (i.e. std::vector<uint8_t>), e.g. the Playground's "Hello World" sample (as of 2024/10/22 22:30 UTC):

static Hello decode(std::vector<uint8_t> sourceBuffer);
static size_t decodeInto(std::vector<uint8_t> sourceBuffer, Hello& target);

That can be quite inefficient depending on the input vector's size, compiler optimizations and/or the caller's use of e.g. std::move().
In addition, given that those calls boil down to calling the overloads that take const uint8_t* sourceBuffer anyway, it seems that there isn't any actual need for copying the input vector and manipulating a local mutable version of it.

Therefore, I'd like to submit this patch to the C++ code generator which changes the input vector copy (i.e. std::vector<uint8_t>) into a const reference (i.e. const std::vector<uint8_t>&).
When applied to the "Hello world" example of the playground, the patched bebopc would generate methods that have the following prototypes:

static Hello decode(const std::vector<uint8_t>& sourceBuffer);
static size_t decodeInto(const std::vector<uint8_t>& sourceBuffer, Hello& target);

Thank you in advance for considering this PR!

…ods prototypes from taking a `std::vector<uint8_t>` copy to taking a `const std::vector<uint8_t>&` const reference
@andrewmd5
Copy link
Contributor

Thank you for the PR. Just a question - the compiler generates:

static size_t decodeInto(const uint8_t* sourceBuffer, size_t sourceBufferSize, Hello& target) {
    ::bebop::Reader reader{sourceBuffer, sourceBufferSize};
    return Hello::decodeInto(reader, target);
  }

This shouldn't incur any copy overhead and gives you better flexibility, does this not align with your use case?

@maddouri
Copy link
Author

maddouri commented Oct 22, 2024

This shouldn't incur any copy overhead and gives you better flexibility, does this not align with your use case?

You're right. However, IMO, there seems to be some sort of "asymmetry" in the generated code's interface: i.e. I find it "surprising" to use a reference in the "encode" directions, e.g.

static size_t encodeInto(const Hello& message, std::vector<uint8_t>& targetBuffer);

... and then use a copy in the "decode" direction, e.g.

static size_t decodeInto(std::vector<uint8_t> sourceBuffer, Hello& target);

A careful reading of the generated code might prevent some developer from making the mistake of calling the overload that results in a copy, but I would still argue that the generated code should be be as consistent and "unsurprising" as possible.

As a sidenote, and to keep up with that "symmetry" theme, one could also wonder about the lack of "encoding" methods that would take uint8_t* destinationBuffer, size_t destinationBufferSize... but that could be a topic for another PR 😉

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

Successfully merging this pull request may close these issues.

2 participants