Skip to content

Commit

Permalink
Merge pull request eclipse-iceoryx#492 from orecham/iox2-490-loan-sli…
Browse files Browse the repository at this point in the history
…ces-from-cxx-api

[eclipse-iceoryx#490] Enable loan_slice APIs in CXX bindings
  • Loading branch information
orecham authored Nov 7, 2024
2 parents e7d1f59 + 4dfb681 commit 810fb87
Show file tree
Hide file tree
Showing 23 changed files with 1,020 additions and 146 deletions.
6 changes: 5 additions & 1 deletion doc/release-notes/iceoryx2-unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Add `PeriodicTimer` into POSIX building blocks [#425](https://github.com/eclipse-iceoryx/iceoryx2/issues/425)
* Developer permissions for resources [#460](https://github.com/eclipse-iceoryx/iceoryx2/issues/460)
* Add `--send-copy` flag to Benchmark to consider mem operations [#483](https://github.com/eclipse-iceoryx/iceoryx2/issues/483)
* Support for slices in the C++ bindings [#490](https://github.com/eclipse-iceoryx/iceoryx2/issues/490)

### Bugfixes

Expand Down Expand Up @@ -56,7 +57,10 @@
conflicts when merging.
-->

* Example text [#1](https://github.com/eclipse-iceoryx/iceoryx2/issues/1)
* APIs to support slices in the C++ bindings [#490](https://github.com/eclipse-iceoryx/iceoryx2/issues/490)
* Rename `iox2_publisher_loan` to `iox2_publisher_loan_slice_uninit` [#490](https://github.com/eclipse-iceoryx/iceoryx2/issues/490)
1. C always loans slices, for a single element, specify the
`number_of_elements` to be 1

### API Breaking Changes

Expand Down
2 changes: 1 addition & 1 deletion examples/c/domains/src/publisher.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ int main(int argc, char** argv) {

// loan sample
iox2_sample_mut_h sample = NULL;
if (iox2_publisher_loan(&publisher, NULL, &sample) != IOX2_OK) {
if (iox2_publisher_loan_slice_uninit(&publisher, NULL, &sample, 1) != IOX2_OK) {
printf("Failed to loan sample\n");
goto drop_publisher;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/c/publish_subscribe/src/publisher.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ int main(void) {

// loan sample
iox2_sample_mut_h sample = NULL;
if (iox2_publisher_loan(&publisher, NULL, &sample) != IOX2_OK) {
if (iox2_publisher_loan_slice_uninit(&publisher, NULL, &sample, 1) != IOX2_OK) {
printf("Failed to loan sample\n");
goto drop_publisher;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ int main(void) {

// loan sample
iox2_sample_mut_h sample = NULL;
if (iox2_publisher_loan(&publisher, NULL, &sample) != IOX2_OK) {
if (iox2_publisher_loan_slice_uninit(&publisher, NULL, &sample, 1) != IOX2_OK) {
printf("Failed to loan sample\n");
goto drop_publisher;
}
Expand Down
20 changes: 10 additions & 10 deletions examples/cxx/publish_subscribe_dynamic_data/src/publisher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,25 @@ auto main() -> int {
.open_or_create()
.expect("successful service creation/opening");

uint64_t worst_case_memory_size = 1024; // NOLINT
auto publisher = service.publisher_builder()
.max_slice_len(worst_case_memory_size)
.create()
.expect("successful publisher creation");
// Since the payload type is uint8_t, this number is the same as the number of bytes in the payload.
// For other types, number of bytes used by the payload will be max_slice_len * sizeof(Payload::ValueType)
const uint64_t maximum_elements = 1024; // NOLINT
auto publisher =
service.publisher_builder().max_slice_len(maximum_elements).create().expect("successful publisher creation");

auto counter = 1;
auto counter = 0;

while (node.wait(CYCLE_TIME).has_value()) {
counter += 1;

auto required_memory_size = (8 + counter) % 16; // NOLINT
const auto required_memory_size = (counter % 16) + 1; // NOLINT
auto sample = publisher.loan_slice_uninit(required_memory_size).expect("acquire sample");
sample.write_from_fn([&](auto byte_idx) { return (byte_idx + counter) % 255; }); // NOLINT

auto initialized_sample = assume_init(std::move(sample));
send(std::move(initialized_sample)).expect("send successful");

std::cout << "Send sample " << counter << "..." << std::endl;
std::cout << "Send sample " << counter << " with " << required_memory_size << " bytes..." << std::endl;

counter++;
}

std::cout << "exit" << std::endl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "iox2/service_type.hpp"

#include <cstdint>
#include <iomanip>
#include <iostream>

constexpr iox::units::Duration CYCLE_TIME = iox::units::Duration::fromSeconds(1);
Expand All @@ -35,9 +36,10 @@ auto main() -> int {
while (node.wait(CYCLE_TIME).has_value()) {
auto sample = subscriber.receive().expect("receive succeeds");
while (sample.has_value()) {
std::cout << "received " << sample->payload().size() << " bytes: ";
for (auto byte : sample->payload()) {
std::cout << std::hex << byte << " ";
auto payload = sample->payload();
std::cout << "received " << std::dec << static_cast<int>(payload.number_of_bytes()) << " bytes: ";
for (auto byte : payload) {
std::cout << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(byte) << " ";
}
std::cout << std::endl;
sample = subscriber.receive().expect("receive succeeds");
Expand Down
17 changes: 10 additions & 7 deletions examples/rust/publish_subscribe_dynamic_data/publisher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,27 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.publish_subscribe::<[u8]>()
.open_or_create()?;

let worst_case_memory_size = 1024;
let maximum_elements = 1024;
let publisher = service
.publisher_builder()
.max_slice_len(worst_case_memory_size)
.max_slice_len(maximum_elements)
.create()?;

let mut counter = 1;
let mut counter = 0;

while node.wait(CYCLE_TIME).is_ok() {
counter += 1;

let required_memory_size = (8 + counter) % 16;
let required_memory_size = (counter % 16) + 1;
let sample = publisher.loan_slice_uninit(required_memory_size)?;
let sample = sample.write_from_fn(|byte_idx| ((byte_idx + counter) % 255) as u8);

sample.send()?;

println!("Send sample {} ...", counter);
println!(
"Send sample {} with {} bytes...",
counter, required_memory_size
);

counter += 1;
}

println!("exit");
Expand Down
150 changes: 127 additions & 23 deletions iceoryx2-ffi/cxx/include/iox/slice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,142 @@
#include "iox/assertions_addendum.hpp"

#include <cstdint>
#include <type_traits>

namespace iox {

/// @brief A class representing a slice of contiguous elements of type T.
///
/// A Slice provides a view into a contiguous sequence of elements without owning the memory.
/// It allows for efficient access and iteration over a portion of a contiguous data structure.
///
/// @tparam T The type of elements in the slice. Can be const-qualified for read-only slices.
template <typename T>
class Slice {
public:
using Iterator = T*;
using ConstIterator = const T*;
using ValueType = T;

auto size() const -> uint64_t {
IOX_TODO();
}
auto operator[](const uint64_t n) const -> const T& {
IOX_TODO();
}
auto operator[](const uint64_t n) -> T& {
IOX_TODO();
}
auto begin() -> Iterator {
IOX_TODO();
}
auto begin() const -> ConstIterator {
IOX_TODO();
}
auto end() -> Iterator {
IOX_TODO();
}
auto end() const -> ConstIterator {
IOX_TODO();
}
using ValueType = std::remove_const_t<T>;

/// @brief Constructs a Slice object.
/// @param[in] data Pointer to the beginning of the data.
/// @param[in] number_of_elements The number of elements in the slice.
Slice(T* data, uint64_t number_of_elements);

/// @brief Returns the total number of bytes occupied by the slice.
/// @return The number of bytes occupied by the slice, rounded up to the nearest alignment boundary.
auto number_of_bytes() const -> uint64_t;

/// @brief Returns the number of elements in the slice.
/// @return The number of elements in the slice.
auto number_of_elements() const -> uint64_t;

/// @brief Accesses the element at the specified index (const version).
/// @param[in] n The index of the element to access.
/// @return A const reference to the element at the specified index.
/// @pre The index must be less than the number of elements in the slice.
auto operator[](uint64_t n) const -> const ValueType&;

/// @brief Accesses the element at the specified index (non-const version).
/// @param[in] n The index of the element to access.
/// @return A reference to the element at the specified index.
/// @pre The index must be less than the number of elements in the slice.
auto operator[](uint64_t n) -> std::conditional_t<std::is_const_v<T>, const ValueType&, ValueType&>;

/// @brief Returns an iterator to the beginning of the slice (const version).
/// @return An iterator pointing to the first element of the slice.
auto begin() const -> ConstIterator;

/// @brief Returns an iterator to the beginning of the slice (non-const version).
/// @return An iterator pointing to the first element of the slice.
auto begin() -> Iterator;

/// @brief Returns an iterator to the end of the slice (const version).
/// @return An iterator pointing one past the last element of the slice.
auto end() const -> ConstIterator;

/// @brief Returns an iterator to the end of the slice (non-const version).
/// @return An iterator pointing one past the last element of the slice.
auto end() -> Iterator;

/// @brief Returns a pointer to the underlying data of the slice (const version).
/// @return A pointer to the first element of the slice.
auto data() const -> ConstIterator;

/// @brief Returns a pointer to the underlying data of the slice (non-const version).
/// @return A pointer to the first element of the slice.
auto data() -> Iterator;

private:
T* m_data;
uint64_t m_number_of_elements;
};

template <typename T>
using MutableSlice = Slice<T>;

template <typename T>
using ImmutableSlice = Slice<const T>;

template <typename T>
Slice<T>::Slice(T* data, uint64_t number_of_elements)
: m_data { data }
, m_number_of_elements { number_of_elements } {
static_assert(!std::is_same_v<T, void>, "Slice<void> is not allowed");
}

template <typename T>
auto Slice<T>::number_of_bytes() const -> uint64_t {
return (sizeof(ValueType) * m_number_of_elements + alignof(ValueType) - 1) & ~(alignof(ValueType) - 1);
}

template <typename T>
auto Slice<T>::number_of_elements() const -> uint64_t {
return m_number_of_elements;
}

template <typename T>
auto Slice<T>::operator[](const uint64_t n) const -> const ValueType& {
IOX_ASSERT(n < m_number_of_elements, "Index out of bounds");
return *(m_data + n);
}

template <typename T>
auto Slice<T>::operator[](const uint64_t n) -> std::conditional_t<std::is_const_v<T>, const ValueType&, ValueType&> {
IOX_ASSERT(n < m_number_of_elements, "Index out of bounds");
return *(m_data + n);
}

template <typename T>
auto Slice<T>::begin() -> Iterator {
return m_data;
}

template <typename T>
auto Slice<T>::begin() const -> ConstIterator {
return m_data;
}

template <typename T>
auto Slice<T>::end() -> Iterator {
return m_data + m_number_of_elements;
}

template <typename T>
auto Slice<T>::end() const -> ConstIterator {
return m_data + m_number_of_elements;
}

template <typename T>
auto Slice<T>::data() -> Iterator {
return m_data;
}

template <typename T>
auto Slice<T>::data() const -> ConstIterator {
return m_data;
}

template <typename>
struct IsSlice {
static constexpr bool VALUE = false;
Expand Down
31 changes: 31 additions & 0 deletions iceoryx2-ffi/cxx/include/iox2/payload_info.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2024 Contributors to the Eclipse Foundation
//
// See the NOTICE file(s) distributed with this work for additional
// information regarding copyright ownership.
//
// This program and the accompanying materials are made available under the
// terms of the Apache Software License 2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
// which is available at https://opensource.org/licenses/MIT.
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

#ifndef IOX2_PAYLOAD_INFO_HPP
#define IOX2_PAYLOAD_INFO_HPP

#include "iox/slice.hpp"

namespace iox2 {

template <typename T>
struct PayloadInfo {
using ValueType = T;
};

template <typename T>
struct PayloadInfo<iox::Slice<T>> {
using ValueType = typename iox::Slice<T>::ValueType;
};

} // namespace iox2
#endif
5 changes: 4 additions & 1 deletion iceoryx2-ffi/cxx/include/iox2/port_factory_publisher.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "iox/builder_addendum.hpp"
#include "iox/expected.hpp"
#include "iox2/internal/iceoryx2.hpp"
#include "iox2/payload_info.hpp"
#include "iox2/publisher.hpp"
#include "iox2/service_type.hpp"
#include "iox2/unable_to_deliver_strategy.hpp"
Expand Down Expand Up @@ -71,7 +72,9 @@ PortFactoryPublisher<S, Payload, UserHeader>::create() && -> iox::expected<Publi
iox2_port_factory_publisher_builder_unable_to_deliver_strategy(
&m_handle, static_cast<iox2_unable_to_deliver_strategy_e>(iox::into<int>(value)));
});
m_max_slice_len.and_then([](auto) { IOX_TODO(); });
m_max_slice_len
.and_then([&](auto value) { iox2_port_factory_publisher_builder_set_max_slice_len(&m_handle, value); })
.or_else([&]() { iox2_port_factory_publisher_builder_set_max_slice_len(&m_handle, 1); });
m_max_loaned_samples.and_then(
[&](auto value) { iox2_port_factory_publisher_builder_set_max_loaned_samples(&m_handle, value); });

Expand Down
Loading

0 comments on commit 810fb87

Please sign in to comment.