-
Notifications
You must be signed in to change notification settings - Fork 5
WIP: RSDK-11082: Remove instances of C-style pointer manipulation #30
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
base: main
Are you sure you want to change the base?
Conversation
| return boost::span{bytes.get(), size}; | ||
| } | ||
|
|
||
| static constexpr deleter_type array_delete_deleter = [](unsigned char* ptr) { delete[] ptr; }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that
- it is UB to
freememory that wasnew'd - the
std::default_deletetemplate parameter forunique_ptr<T[]>already doesdelete[]
https://en.cppreference.com/w/cpp/memory/default_delete.html
|
|
||
| std::vector<unsigned char> RGBPointsToPCD(std::shared_ptr<ob::Frame> frame, float scale) { | ||
| int numPoints = frame->dataSize() / sizeof(OBColorPoint); | ||
| assert(frame->is<ob::PointsFrame>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this pattern comes up more often we could write a FrameView<T> class which performs the following asserts and returns a span of the data already set to type T
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what kind of error message is produced when an assert fails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It includes the line like:
orbbec.cpp:152 frame->is<ob::PointsFrame>() assert failed
So it includes the line number and string of the failing assert.
| std::memcpy(rawBuf, encodedData.data(), encodedData.size()); | ||
| raw_camera_image rawBuf(encodedData.size()); | ||
|
|
||
| std::copy(encodedData.begin(), encodedData.end(), rawBuf.span().begin()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that most standard library implementations of std::copy will fall back to a compiler intrinsic version of memcpy or memmove when operating on contiguous iterators of arithmetic types
| } | ||
|
|
||
| static constexpr deleter_type array_delete_deleter = [](unsigned char* ptr) { delete[] ptr; }; | ||
| boost::span<unsigned char> span() noexcept { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not critical at all, but could we use std::uint8_t instead of unsigned char to express bytes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that works!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SebastianMunozP I was looking into this and it seems like we may want to use either unsigned char or std::byte, as per these guidelines: https://stackoverflow.com/a/77097674 but they advise against std::uint8_t
I believe this project is C++17 so we could use std::byte instead of unsigned char, do you have thoughts or preferences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, didn't know about std::byte :) , I agree, let's go with that then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just added some changes using std::byte. unfortunately since the Viam SDK is still on C++14 there is no access to std::byte so we end up having to convert back to unsigned char. Please let me know your thoughts, we can keep it this way or revert to the previous diff using unsigned char
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, in that case let's just go back to unsigned char and try this change maybe in the future.
| assert(frame->is<ob::PointsFrame>()); | ||
| assert(frame->getFormat() == OBFormat::OB_FORMAT_RGB_POINT); | ||
|
|
||
| auto pointSpan = boost::span{reinterpret_cast<OBColorPoint*>(frame->data()), frame->dataSize() / sizeof(OBColorPoint)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do we ensure that the span's underlying bytes do not get freed while using it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hexbabe that's the responsibility of the calling code; span is a non-owning reference or "view" type, meaning it provides a different view or interface upon some underlying data without taking responsibility for the lifetime of the data. Similar to string_view, or language-level references (int i; int& j = i;).
see https://stackoverflow.com/a/45723820 for further discussion
|
|
||
| std::vector<unsigned char> RGBPointsToPCD(std::shared_ptr<ob::Frame> frame, float scale) { | ||
| int numPoints = frame->dataSize() / sizeof(OBColorPoint); | ||
| assert(frame->is<ob::PointsFrame>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like using asserts, but in this case I would prefer to detect the case of these two asserts and log it and early return. I think it would be better to handle this by looking at logs than forcing the module to terminate.
Cc. @nicksanford in case you want to weigh in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- If the assert fails does it terminate the os process?
- If it terminates the os process does Viam server get an unambiguous log that would tell us immediately why the process terminated?
- Is it impossible for the assertion to ever fail in practice given the current state of the code?
If the answers to all those questions are 'yes' then this is fine.
If the answer to any of them is no then I'll need a justification for why this asset is here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- yes (via calling std::abort())
- yes (we will get a message stating the file/line of the assert and the assert that failed)
- I tested this module and the assertions did not failed
It's worth noting that as the code is at the moment, these asserts are being removed from the binary since this is a release build. I enabled them by creating a debug build and making sure that I could hit asserts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having assets fire in release builds sounds good to me.
I'd also be ok with us writing our own assert function. Whichever you feel is best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in the current code as is the assert wouldn't get hit unless someone called RGBPointsToPCD(not_an_rgb_point_frame). The asserts are there to show how we can check type safety given that we are dealing with polymorphic data but in this function only one type of input is acceptable.
Per another comment #30 (comment) the use of assert here was meant to sketch a general procedure that could be used if this pattern keeps coming up. Namely, we have an ob::Frame* frame, and we want to get a range which refers to a definite underlying type. In this case it would be nice to be able to just do
void f(std::shared_ptr<ob::Frame> frame){
FrameView<OBColorPoint> frameView(frame);
}and we would expect this to be an error if frame were not in fact an OB_FORMAT_RGB_POINT, similar to how some standard library containers have asserts or bounds checking in debug builds.
I think assert or throw is the right behavior here; this will fail loudly with a meaningful log message and viam-server can restart the module. An early return would result in further control flow being executed with an empty vector, which could be easy to miss.
A possible option, since we already use Boost, is Boost.Assert. This has the advantage of letting us
- keep asserts in non-debug builds independent of
NDEBUG - write a different assertion processing function (eg, log error, throw exception) rather than the
assertbehavior of callingabort
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea, let's do that, let's change the asserts for BOOST_ASSERT
hexbabe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approving, but mostly just read over and asked questions for learning
| assert(frame->is<ob::PointsFrame>()); | ||
| assert(frame->getFormat() == OBFormat::OB_FORMAT_RGB_POINT); | ||
|
|
||
| auto pointSpan = boost::span{reinterpret_cast<OBColorPoint*>(frame->data()), frame->dataSize() / sizeof(OBColorPoint)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how do we ensure that the span's underlying bytes do not get freed while using it?
| } | ||
| auto depthPixels = boost::span{reinterpret_cast<uint16_t const*>(data), height * width}; | ||
|
|
||
| std::transform(depthPixels.begin(), depthPixels.end(), m.begin(), [](uint16_t mi) { return mi * mmToMeterMultiple; }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is sick
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may i recommend this for inspo 🙂
https://youtu.be/W2tWOdzgXHA?t=133
|
|
||
| std::vector<unsigned char> RGBPointsToPCD(std::shared_ptr<ob::Frame> frame, float scale) { | ||
| int numPoints = frame->dataSize() / sizeof(OBColorPoint); | ||
| assert(frame->is<ob::PointsFrame>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what kind of error message is produced when an assert fails?
|
Actually I'll hold off approval until manual tests are reported to ensure no regressions |
hexbabe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blocked on test writeup
This PR modernizes some of the older C-style pointer code in favor of modern approaches using
boost::spanand range-based for loops or algorithms from<algorithm>. I will annotate the PR below with comments; feel free to suggest if you think they should be put into the code as comments alsoMarking as draft for now pending some further testing