Skip to content

Conversation

@lia-viam
Copy link
Contributor

This PR modernizes some of the older C-style pointer code in favor of modern approaches using boost::span and 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 also

Marking as draft for now pending some further testing

return boost::span{bytes.get(), size};
}

static constexpr deleter_type array_delete_deleter = [](unsigned char* ptr) { delete[] ptr; };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that


std::vector<unsigned char> RGBPointsToPCD(std::shared_ptr<ob::Frame> frame, float scale) {
int numPoints = frame->dataSize() / sizeof(OBColorPoint);
assert(frame->is<ob::PointsFrame>());
Copy link
Contributor Author

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

Copy link
Collaborator

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?

Copy link
Contributor

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());
Copy link
Contributor Author

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 {
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that works!

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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)};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this :)

Copy link
Collaborator

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?

Copy link
Contributor Author

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>());
Copy link
Contributor

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. If the assert fails does it terminate the os process?
  2. If it terminates the os process does Viam server get an unambiguous log that would tell us immediately why the process terminated?
  3. 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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. yes (via calling std::abort())
  2. yes (we will get a message stating the file/line of the assert and the assert that failed)
  3. 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.

Copy link
Collaborator

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.

Copy link
Contributor Author

@lia-viam lia-viam Aug 5, 2025

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 assert behavior of calling abort

Copy link
Contributor

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

Copy link
Collaborator

@hexbabe hexbabe left a 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)};
Copy link
Collaborator

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; });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is sick

Copy link
Contributor Author

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>());
Copy link
Collaborator

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?

@hexbabe
Copy link
Collaborator

hexbabe commented Aug 7, 2025

Actually I'll hold off approval until manual tests are reported to ensure no regressions

@hexbabe hexbabe self-requested a review August 7, 2025 20:06
Copy link
Collaborator

@hexbabe hexbabe left a 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

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.

4 participants