6
\$\begingroup\$

I have a function that takes an unsigned value and transforms it into a std::vector<std::uint8_t>. The requirements (and post conditions) are as follows:

  1. The code must utilize "modern C++" up to C++14
  2. The focus is on readability/maintainability first, performance second
  3. The most significant byte must be at index 0.
  4. The lease significant byte must be at index size()-1.
  5. The return type cannot be changed; this is a requirement stemming from other code not shown in this context (larger application requirement). Also avoids a conversion from one container to std::vector.
  6. The resulting vector must be as compact as possible. For values that decompose into fewer bytes than sizeof(unsigned), the resulting vector must not contain unnecessary MSB's (that is, zero value bytes).
  7. The code must be endianness-agnostic and portable to various other architectures and platforms.

Here is the current implementation I have:

using BinaryStreamByteVector = std::vector<std::uint8_t>;

BinaryStreamByteVector CreateMultiByteInteger(unsigned value)
{
    BinaryStreamByteVector value_bytes;

    while (value != 0)
    {
        std::uint8_t byte = value & 0xFF;
        value_bytes.insert(value_bytes.begin(), byte);

        value >>= 8;
    }

    return value_bytes;
}

(Live Sample on Coliru)

I can't think of a better implementation for CreateMultiByteInteger, so I'm hoping for other ideas that meet the requirements above.

\$\endgroup\$
6
  • \$\begingroup\$ Eh... To me it looks very good as it already is. Pretty performant, well written with proper naming and whitespace. Would you accept this as an answer? Our Meta policy accepts "your code is fine" answers. \$\endgroup\$ Commented Jan 11, 2017 at 16:16
  • \$\begingroup\$ What would be overkill here but ideal is probably a template function returning a const array or a const std:: array (for retaining size information), which varies the size of the array returned using the sizeof operator on unsigned. That would be absolutely performant, I guess. The algorithm is perfect. You even include the masking of the higher-order bits. Unfortunately, you have a restriction on the return type, so this wouldn't help. However, maybe the idea of making it a template function will still help? \$\endgroup\$ Commented Jan 11, 2017 at 16:29
  • 1
    \$\begingroup\$ I guess what I was hoping for was a different approach, like masking from MSB to LSB (I'm currently doing the reverse of that). But I wasn't able to find an easy way to do that. Not trying to overthink or over engineer this; but definitely wanted to try getting different perspectives. Thanks guys; I'd be happy to accept an answer that says "It's good enough"; gives me an opportunity to check up-votes of that as a way to measure for general consensus. \$\endgroup\$ Commented Jan 11, 2017 at 16:34
  • 1
    \$\begingroup\$ Does this need to be endianness-independent? You state what your requirements are for the resulting vector, but what about the input integer? If you can assume a particular endianness (e.g., because you're only targeting one architecture and don't care if your code is portable), then you can make this a lot faster without adding much complexity. Otherwise, you should probably stick with the general approach you've used here, unless speed is a big concern (which you indicate it is not). \$\endgroup\$
    – Cody Gray
    Commented Jan 11, 2017 at 16:40
  • 1
    \$\begingroup\$ @CodyGray, It's always better to be independent of as much stuff as possible, especially if the code needs to be portable and performance isn't a concern (but thankfully you can always assume the standard library). \$\endgroup\$ Commented Jan 11, 2017 at 16:43

1 Answer 1

1
\$\begingroup\$

Observations

Your code is beautiful (proper naming conventions followed, proper whitespace usage, very readable. I particularly like the type-aliasing, which helps make the whole thing that much more readable than a typedef or #define). I cannot personally think of any more ways to make your code more readable, however, I do have 1 suggestion.

The algorithm you currently follow is standard and ideal (you even have proper masking of higher-order bits); and in my honest opinion any other algorithm would be tantamount to obfuscation; but if you assume that the code will only ever run on a particular architecture, you could probably unroll the loop.

Suggestion

  1. Make your function parameter const. Although C++ is call-by-value, making it explicitly const and mutating a local copy of the variable is never a bad idea, and it does make your function contract that much clearer.

  2. On the performance side, since on any reasonable computer unsigned will have maximally 16 bytes (64 bits of 8 bytes is the case for all common architectures, except for probably some microcontrollers which will have less, even as less as 1 byte) (note that the upcoming RISCV architecture does have a native 16-byte (128-bit) integral data type, but the 64-bit one is the default), you can easily make this function a template, such that the computation of the byte vector can be done at compile time without much overhead and incur no runtime overhead.

    You can probably even pre-set the capacity of your std::vector utilising sizeof(unsigned).

    Although I'm not sure if this suggestion will work in the constraints of your application.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Ironic that you say here "since on any reasonable computer unsigned will have maximally 4 bytes", yet just replied to me with a comment that says: "It's always better to be independent of as much stuff as possible". While portability has its virtues, I don't agree that it is "always" better to write portable code, and clearly you don't either. :-) \$\endgroup\$
    – Cody Gray
    Commented Jan 11, 2017 at 16:46
  • \$\begingroup\$ Eh... that was a typo. I meant 8 bytes. I'll fix it right now. \$\endgroup\$ Commented Jan 11, 2017 at 17:00
  • \$\begingroup\$ @CodyGray Fixed now :p. \$\endgroup\$ Commented Jan 11, 2017 at 17:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.