user2654735
user2654735

Reputation: 323

c++ pass by reference safely and compile time checking on size

SpiDeviceDriver::SPI_Error SpiDeviceDriver::SPI_ReadBytes(
   quint32          size_,
   QVector<quint8>& rxData_
)
{
     //Get data and fill QVector<quint8> with data
}

I'm trying to call a class function (from another class) and pass in a QVector, then fill it with data.

  1. I prefer to just pass in the QVector alone (without the quint32 size parameter) and then figure out the size from that and fill it with data according to its size. However, if the passed in QVector is size 0, I'd either have to assume it is meant to be size 1, creating a new spot for the data, or throw/handle the error at run-time, which i'd rather not do. Compile time error checking would be much better. Is there a better way to do this?

  2. I guess you could pass in a quint32 size_ parameter, then forget what the size of the QVector is and force the resizing to be that size. This seems awkward as well

Note: I've been instructed by my boss to make every function return an enum error code, so just using a size_ and creating a vector, then returning that data is not an option.

Upvotes: 0

Views: 128

Answers (2)

user4842163
user4842163

Reputation:

That coding standard to return an error code with every function/method really, really sucks for C++. If you can find a way to persuade otherwise diplomatically, I would suggest trying.

You can use thread-local storage to set and retrieve global errors efficiently on a per-thread basis that you can poll any time. You also have exception-handling in C++ and can translate exceptions into error codes at appropriate API/transaction boundaries. It would also still be better to accept a contextual parameter by reference with every function where you can set the error code through it than be forced into returning error codes for everything.

That said, I've been there and done that with shoddy legacy-style coding standards.

So if you have no other option, I would suggest no to your idea of making the function work on a pre-sized version of rxData_ only.

If you are favoring that kind of output parameter type of design, strive to make it output-only when you can. It'll give your design a greater consistency. Don't get fancy with it. Pretend it is the return value. If you pretend that, rxData_ doesn't provide the function with any input information and you'd need size_ anyway.

Don't double up its responsibility as both input and output when you're using the reference like it's an emulated return value even though it's a parameter. That tends to lead to a confusing design and it'll make it difficult to make up your mind about it, so I'd suggest keeping both parameters and resizing the vector accordingly inside the function to match.

If it's an empty vector or one with a different size than intended, you would have had to resize it anyway outside, so you're not saving cycles. If it was the same size, then calling resize with the same size would generally be trivial as it would just skip the process.

Upvotes: 0

Peter - Reinstate Monica
Peter - Reinstate Monica

Reputation: 16049

So you want to read from a data source into the given vector. Apparently the application knows at run time how many bytes are expected, hence the idea of a size "in" parameter. But that is redundant: QVector (cf. http://doc.qt.io/qt-5/qvector.html#QVector-2) has a constructor which takes a size argument and a size() member function. Therefore, the expected number of bytes can be communicated by passing a vector of the proper size.

If that is deemed not explicit and obvious enough nothing except a certain redundancy speaks against a size parameter though. It would also bundle vector initialization code in the function.

Which leads to another alternative: To create and return a vector from the function (as value). Whether that is too costly depends on QVector's ability to be "moved" (doesn't look like it) or the compiler's return value optimization capabilities.

Error handling is another issue. The caller could check for short reads by inspecting the vector's size upon return. Shorter than required sizes indicate an error. If different reasons for error must be communicated there is a choice between exceptions or, indeed, error return values. The latter would imply to "return" the vector through some indirecting parameter.

Update regarding the special case of a request to read 0 bytes: This could also be handled as a legit (possibly diagnostic) request, as is done by the read() of the C standard library, cf. http://linux.die.net/man/2/read. If the line is ok, the return enum is "success" and the function does nothing and returns an empty vector. If an error condition is detected (no idea whether that's actually possible), the appropriate error is returned. Treating 0 byte read requests as NOPs fits nicely with many algorithms, the same way empty strings or vectors do.

Upvotes: 1

Related Questions