Skip to content

Commit

Permalink
Add possibility to use ConsumableSpan in processBulk signature.
Browse files Browse the repository at this point in the history
  • Loading branch information
drslebedev committed Feb 15, 2024
1 parent f00b458 commit f7e9adf
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 40 deletions.
11 changes: 6 additions & 5 deletions core/include/gnuradio-4.0/Block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,9 @@ class Block : public lifecycle::StateMachine<Derived>, protected std::tuple<Argu

//
using RatioValue = std::conditional_t<Resampling::kIsConst, const gr::Size_t, gr::Size_t>;
A<RatioValue, "numerator", Doc<"Top of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)">, Limits<1UL, std::numeric_limits<RatioValue>::max()>> numerator = Resampling::kNumerator;
A<RatioValue, "denominator", Doc<"Bottom of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)">, Limits<1UL, std::numeric_limits<RatioValue>::max()>> denominator = Resampling::kDenominator;
A<RatioValue, "numerator", Doc<"Top of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)">, Limits<1UL, std::numeric_limits<RatioValue>::max()>> numerator = Resampling::kNumerator;
A<RatioValue, "denominator", Doc<"Bottom of resampling ratio (<1: Decimate, >1: Interpolate, =1: No change)">, Limits<1UL, std::numeric_limits<RatioValue>::max()>> denominator
= Resampling::kDenominator;
using StrideValue = std::conditional_t<StrideControl::kIsConst, const gr::Size_t, gr::Size_t>;
A<StrideValue, "stride", Doc<"samples between data processing. <N for overlap, >N for skip, =0 for back-to-back.">> stride = StrideControl::kStride;

Expand Down Expand Up @@ -1338,9 +1339,9 @@ class Block : public lifecycle::StateMachine<Derived>, protected std::tuple<Argu
// work::Status::OK) : work::Status::ERROR };
return { requested_work, ports_status.in_samples, success ? work::Status::OK : work::Status::ERROR };
} // processOne(...) handling
// else {
// static_assert(gr::meta::always_false<Derived>, "neither processBulk(...) nor processOne(...) implemented");
// }
else {
static_assert(gr::meta::always_false<Derived>, "neither processBulk(...) nor processOne(...) implemented");
}
return { requested_work, 0UZ, work::Status::ERROR };
} // end: work_return_t work_internal() noexcept { ..}

Expand Down
76 changes: 58 additions & 18 deletions core/include/gnuradio-4.0/BlockTraits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,20 +281,61 @@ concept can_processMessagesForPortConsumableSpan = requires(TBlock &block, TPort
template<typename TBlock, typename TPort>
concept can_processMessagesForPortStdSpan = requires(TBlock &block, TPort &inPort, std::span<const Message> msgSpan) { block.processMessages(inPort, msgSpan); };

// clang-format off
namespace detail {

template<typename T>
struct dummy_input_span : std::span<const T> { // NOSONAR
dummy_input_span(const dummy_input_span &) = delete; // NOSONAR
dummy_input_span(dummy_input_span &&) noexcept; // NOSONAR
constexpr void consume(std::size_t) noexcept;
struct DummyConsumableSpan {
using value_type = typename std::remove_cv_t<T>;
using iterator = typename std::span<const T>::iterator;

private:
std::span<const T> internalSpan; // Internal span, used for fake implementation

public:
DummyConsumableSpan() = default;
DummyConsumableSpan(const DummyConsumableSpan& other) = default;
DummyConsumableSpan& operator=(const DummyConsumableSpan& other) = default;
DummyConsumableSpan(DummyConsumableSpan&& other) noexcept = default;
DummyConsumableSpan& operator=(DummyConsumableSpan&& other) noexcept = default;
~DummyConsumableSpan() = default;

[[nodiscard]] constexpr iterator begin() const noexcept { return internalSpan.begin(); }
[[nodiscard]] constexpr iterator end() const noexcept { return internalSpan.end(); }
operator const std::span<const T>&() const noexcept { return internalSpan; }
operator std::span<const T>&() noexcept { return internalSpan; }
operator std::span<const T>&&() = delete;

[[nodiscard]] bool consume(std::size_t nSamples) const noexcept { return true; }
};
static_assert(ConsumableSpan<DummyConsumableSpan<int>>);

template<typename T>
struct dummy_output_span : std::span<T> { // NOSONAR
dummy_output_span(const dummy_output_span &) = delete; // NOSONAR
dummy_output_span(dummy_output_span &&) noexcept; // NOSONAR
constexpr void publish(std::size_t) noexcept;
struct DummyPublishableSpan {
using value_type = typename std::remove_cv_t<T>;
using iterator = typename std::span<T>::iterator;

private:
std::span<T> internalSpan; // Internal span, used for fake implementation

public:
DummyPublishableSpan() = default;
DummyPublishableSpan(const DummyPublishableSpan& other) = delete;
DummyPublishableSpan& operator=(const DummyPublishableSpan& other) = delete;
DummyPublishableSpan(DummyPublishableSpan&& other) noexcept = default;
DummyPublishableSpan& operator=(DummyPublishableSpan&& other) noexcept = default;
~DummyPublishableSpan() = default;

[[nodiscard]] constexpr iterator begin() const noexcept { return internalSpan.begin(); }
[[nodiscard]] constexpr iterator end() const noexcept { return internalSpan.end(); }
operator const std::span<T>&() const noexcept { return internalSpan; }
operator std::span<T>&() noexcept { return internalSpan; }

constexpr void publish(std::size_t) noexcept {}
};
static_assert(PublishableSpan<DummyPublishableSpan<int>>);

// clang-format on

struct to_any_vector {
template<typename Any>
Expand Down Expand Up @@ -336,9 +377,9 @@ port_to_processBulk_argument_helper() {

} else if constexpr (Port::kIsSynch) {
if constexpr (Port::kIsInput) {
return static_cast<dummy_input_span<typename Port::value_type> *>(nullptr);
return static_cast<DummyConsumableSpan<typename Port::value_type> *>(nullptr);
} else if constexpr (Port::kIsOutput) {
return static_cast<dummy_output_span<typename Port::value_type> *>(nullptr);
return static_cast<DummyPublishableSpan<typename Port::value_type> *>(nullptr);
}
} else {
if constexpr (Port::kIsInput) {
Expand Down Expand Up @@ -382,20 +423,19 @@ concept can_processBulk = requires(TBlock &n, typename meta::transform_types_nes
*/
template<typename TDerived, std::size_t I>
concept processBulk_requires_ith_output_as_span
= can_processBulk<TDerived>
&& requires(TDerived &d, typename meta::transform_types<detail::dummy_input_span, traits::block::stream_input_port_types<TDerived>>::template apply<std::tuple> inputs,
typename meta::transform_conditional<decltype([](auto j) { return j == I; }), detail::dynamic_span, detail::dummy_output_span,
= can_processBulk<TDerived> && (I < traits::block::stream_output_port_types<TDerived>::size) && (I >= 0)
&& requires(TDerived &d, typename meta::transform_types<detail::DummyConsumableSpan, traits::block::stream_input_port_types<TDerived>>::template apply<std::tuple> inputs,
typename meta::transform_conditional<decltype([](auto j) { return j == I; }), detail::dynamic_span, detail::DummyPublishableSpan,
traits::block::stream_output_port_types<TDerived>>::template apply<std::tuple>
outputs,
typename meta::transform_conditional<decltype([](auto j) { return j == I; }), detail::nothing_you_ever_wanted, detail::dummy_output_span,
typename meta::transform_conditional<decltype([](auto j) { return j == I; }), detail::nothing_you_ever_wanted, detail::DummyPublishableSpan,
traits::block::stream_output_port_types<TDerived>>::template apply<std::tuple>
bad_outputs) {
{
[]<std::size_t... InIdx, std::size_t... OutIdx>(std::index_sequence<InIdx...>,
std::index_sequence<OutIdx...>) -> decltype(d.processBulk(std::get<InIdx>(inputs)..., std::get<OutIdx>(outputs)...)) {
return {};
}(std::make_index_sequence<traits::block::stream_input_port_types<TDerived>::size>(), std::make_index_sequence<traits::block::stream_output_port_types<TDerived>::size>())
detail::can_processBulk_invoke_test(d, inputs, outputs, std::make_index_sequence<stream_input_port_types<TDerived>::size>(),
std::make_index_sequence<stream_output_port_types<TDerived>::size>())
} -> std::same_as<work::Status>;
// TODO: Is this check redundant?
not requires {
[]<std::size_t... InIdx, std::size_t... OutIdx>(std::index_sequence<InIdx...>,
std::index_sequence<OutIdx...>) -> decltype(d.processBulk(std::get<InIdx>(inputs)..., std::get<OutIdx>(bad_outputs)...)) {
Expand Down
4 changes: 2 additions & 2 deletions core/include/gnuradio-4.0/CircularBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ class CircularBuffer
}
~ConsumableInputRange() = default;

[[nodiscard]] constexpr std::size_t size() const noexcept { return _internalSpan.size(); };
[[nodiscard]] constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(T); };
[[nodiscard]] constexpr std::size_t size() const noexcept { return _internalSpan.size(); }
[[nodiscard]] constexpr std::size_t size_bytes() const noexcept { return size() * sizeof(T); }
[[nodiscard]] constexpr bool empty() const noexcept { return _internalSpan.empty(); }
[[nodiscard]] constexpr iterator begin() const noexcept { return _internalSpan.begin(); }
[[nodiscard]] constexpr iterator end() const noexcept { return _internalSpan.end(); }
Expand Down
82 changes: 67 additions & 15 deletions core/test/qa_Block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ static_assert(gr::HasProcessOneFunction<BlockSignaturesProcessOneConst<float>>);
static_assert(gr::HasConstProcessOneFunction<BlockSignaturesProcessOneConst<float>>);
static_assert(!gr::HasProcessBulkFunction<BlockSignaturesProcessOneConst<float>>);

enum class ProcessBulkVariant { VARIANT_SPAN, VARIANT_PUBLISHABLE_SPAN, VARIANT_PUBLISHABLE_SPAN2, VARIANT_CONSUMABLE_SPAN, VARIANT_CONSUMABLE_SPAN2 };
enum class ProcessBulkVariant { SPAN, PUBLISHABLE_SPAN, PUBLISHABLE_SPAN2, CONSUMABLE_SPAN, CONSUMABLE_SPAN2 };

template<typename T, ProcessBulkVariant processVariant>
struct BlockSignaturesProcessBulkSpan : public gr::Block<BlockSignaturesProcessBulkSpan<T, processVariant>> {
Expand All @@ -91,55 +91,107 @@ struct BlockSignaturesProcessBulkSpan : public gr::Block<BlockSignaturesProcessB

gr::work::Status
processBulk(std::span<const T>, std::span<T>)
requires(processVariant == ProcessBulkVariant::VARIANT_SPAN)
requires(processVariant == ProcessBulkVariant::SPAN)
{
// do some bulk-type processing
return gr::work::Status::OK;
}

gr::work::Status
processBulk(std::span<const T>, gr::PublishableSpan auto &)
requires(processVariant == ProcessBulkVariant::VARIANT_PUBLISHABLE_SPAN)
requires(processVariant == ProcessBulkVariant::PUBLISHABLE_SPAN)
{
// do some bulk-type processing
return gr::work::Status::OK;
}

gr::work::Status
processBulk(std::span<const T>, gr::PublishableSpan auto)
requires(processVariant == ProcessBulkVariant::VARIANT_PUBLISHABLE_SPAN2)
requires(processVariant == ProcessBulkVariant::PUBLISHABLE_SPAN2)
{
// do some bulk-type processing
return gr::work::Status::OK;
}

gr::work::Status
processBulk(gr::ConsumableSpan auto, std::span<T>)
requires(processVariant == ProcessBulkVariant::VARIANT_CONSUMABLE_SPAN)
requires(processVariant == ProcessBulkVariant::CONSUMABLE_SPAN)
{
// do some bulk-type processing
return gr::work::Status::OK;
}

gr::work::Status
processBulk(gr::ConsumableSpan auto &, std::span<T>)
requires(processVariant == ProcessBulkVariant::VARIANT_CONSUMABLE_SPAN2)
requires(processVariant == ProcessBulkVariant::CONSUMABLE_SPAN2)
{
// do some bulk-type processing
return gr::work::Status::OK;
}
};

ENABLE_REFLECTION_FOR_TEMPLATE_FULL((typename T, ProcessBulkVariant processVariant), (BlockSignaturesProcessBulkSpan<T, processVariant>), in, out);
using enum ProcessBulkVariant;
static_assert(gr::HasRequiredProcessFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_SPAN>>);
static_assert(!gr::HasProcessOneFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_SPAN>>);
static_assert(!gr::HasConstProcessOneFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_SPAN>>);
static_assert(gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_SPAN>>);
static_assert(gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_PUBLISHABLE_SPAN>>);
static_assert(!gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_PUBLISHABLE_SPAN2>>); // TODO: fix the signature is required to work
static_assert(!gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_CONSUMABLE_SPAN>>); // TODO: fix the signature is required to work
static_assert(!gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, VARIANT_CONSUMABLE_SPAN2>>); // TODO: fix the signature is required to work

static_assert(gr::HasRequiredProcessFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::SPAN>>);
static_assert(!gr::HasProcessOneFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::SPAN>>);
static_assert(!gr::HasConstProcessOneFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::SPAN>>);
static_assert(gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::SPAN>>);
static_assert(gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::PUBLISHABLE_SPAN>>);
static_assert(!gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::PUBLISHABLE_SPAN2>>); // TODO: fix the signature is required to work
static_assert(gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::CONSUMABLE_SPAN>>);
static_assert(gr::HasProcessBulkFunction<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::CONSUMABLE_SPAN2>>);

static_assert(gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::SPAN>, 0>);
static_assert(!gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkSpan<float, ProcessBulkVariant::PUBLISHABLE_SPAN>, 0>);

enum class ProcessBulkTwoOutsVariant { SPAN_SPAN, PUBLISHABLE_SPAN, PUBLISHABLE_PUBLISHABLE, SPAN_PUBLISHABLE };

template<typename T, ProcessBulkTwoOutsVariant processVariant>
struct BlockSignaturesProcessBulkTwoOuts : public gr::Block<BlockSignaturesProcessBulkTwoOuts<T, processVariant>> {
gr::PortIn<T> in{};
gr::PortOut<T> out1{};
gr::PortOut<T> out2{};

gr::work::Status
processBulk(std::span<const T>, std::span<T>, std::span<T>)
requires(processVariant == ProcessBulkTwoOutsVariant::SPAN_SPAN)
{
return gr::work::Status::OK;
}

gr::work::Status
processBulk(std::span<const T>, gr::PublishableSpan auto &, std::span<T>)
requires(processVariant == ProcessBulkTwoOutsVariant::PUBLISHABLE_SPAN)
{
return gr::work::Status::OK;
}

gr::work::Status
processBulk(std::span<const T>, gr::PublishableSpan auto &, gr::PublishableSpan auto &)
requires(processVariant == ProcessBulkTwoOutsVariant::PUBLISHABLE_PUBLISHABLE)
{
return gr::work::Status::OK;
}

gr::work::Status
processBulk(std::span<const T>, std::span<T>, gr::PublishableSpan auto &)
requires(processVariant == ProcessBulkTwoOutsVariant::SPAN_PUBLISHABLE)
{
return gr::work::Status::OK;
}
};

ENABLE_REFLECTION_FOR_TEMPLATE_FULL((typename T, ProcessBulkTwoOutsVariant processVariant), (BlockSignaturesProcessBulkTwoOuts<T, processVariant>), in, out1, out2);

static_assert(gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::SPAN_SPAN>, 0>);
static_assert(gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::SPAN_SPAN>, 1>);
static_assert(!gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::PUBLISHABLE_SPAN>, 0>);
static_assert(gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::PUBLISHABLE_SPAN>, 1>);
static_assert(gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::SPAN_PUBLISHABLE>, 0>);
static_assert(!gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::SPAN_PUBLISHABLE>, 1>);
static_assert(!gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::PUBLISHABLE_PUBLISHABLE>, 0>);
static_assert(!gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::PUBLISHABLE_PUBLISHABLE>, 1>);
static_assert(!gr::traits::block::processBulk_requires_ith_output_as_span<BlockSignaturesProcessBulkTwoOuts<float, ProcessBulkTwoOutsVariant::SPAN_SPAN>, 2>); // out-of-range check

struct InvalidSettingBlock : gr::Block<InvalidSettingBlock> {
std::tuple<int> tuple; // this type is not supported and should cause the checkBlockContracts<T>() to throw
Expand Down

0 comments on commit f7e9adf

Please sign in to comment.