Skip to content

Commit

Permalink
Refactor gr::registerBlock
Browse files Browse the repository at this point in the history
1. A simple `registerBlock` overload for a list of complete block types
was missing. If the given type is a specialized template, then the
`type_name` is transformed into a template argument string and passed to
`addBlockType`.

2. Generalize the `registerBlock` function for a block template with
type template arguments to an arbitrary number of parameters/arguments.

3. Generalize the `registerBlockTT` function to an arbitrary number of
type lists (and corresponding number of parameters for the template
template parameter). The relatively expensive use of std::apply is
replaced by an `outer_product` over all given typelists.

4. Add a comment on how to improve the remaining `registerBlock`
overloads.

5. Add meta::outer_product (lifted from mattkretz/virtest).

Signed-off-by: Matthias Kretz <[email protected]>
  • Loading branch information
mattkretz committed Oct 1, 2024
1 parent 24f8ec5 commit 8b6584b
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 40 deletions.
85 changes: 45 additions & 40 deletions core/include/gnuradio-4.0/Block.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,26 @@ struct BlockParameters : meta::typelist<Types...> {
static constexpr /*vir::fixed_string*/ auto toString() { return detail::encodeListOfTypes<Types...>(); }
};

template<typename TBlock>
inline int registerBlock(auto& registerInstance) {
using namespace vir::literals;
constexpr auto name = vir::refl::class_name<TBlock>;
constexpr auto longname = vir::refl::type_name<TBlock>;
if constexpr (name != longname) {
constexpr auto tmpl = longname.substring(name.size + 1_cw, longname.size - 2_cw - name.size);
registerInstance.template addBlockType<TBlock>(name, tmpl);
} else {
registerInstance.template addBlockType<TBlock>(name, {});
}
return 0;
}

template<typename TBlock0, typename... More>
inline int registerBlock(auto& registerInstance) {
registerBlock<TBlock0>(registerInstance);
return registerBlock<More...>(registerInstance);
}

/**
* This function (and overloads) can be used to register a block with
* the block registry to be used for runtime instantiation of blocks
Expand All @@ -1945,15 +1965,16 @@ struct BlockParameters : meta::typelist<Types...> {
* set these to the values of NTTPs you want to register
* - TBlockParameters -- types that the block can be instantiated with
*/
template<template<typename> typename TBlock, typename... TBlockParameters, typename TRegisterInstance>
inline constexpr int registerBlock(TRegisterInstance& registerInstance) {
auto addBlockType = [&]<typename Type> {
using ThisBlock = TBlock<Type>;
static_assert(!meta::is_instantiation_of<Type, BlockParameters>);
registerInstance.template addBlockType<ThisBlock>(vir::refl::class_name<TBlock<Type>>, vir::refl::type_name<Type>);
};
((addBlockType.template operator()<TBlockParameters>()), ...);
return {};
template<template<typename...> typename TBlock, typename TBlockParameter0, typename... TBlockParameters>
inline int registerBlock(auto& registerInstance) {
using List0 = std::conditional_t<meta::is_instantiation_of<TBlockParameter0, BlockParameters>, TBlockParameter0, BlockParameters<TBlockParameter0>>;
using ThisBlock = typename List0::template apply<TBlock>;
registerInstance.template addBlockType<ThisBlock>(vir::refl::class_name<ThisBlock>, List0::toString());
if constexpr (sizeof...(TBlockParameters) != 0) {
return registerBlock<TBlock, TBlockParameters...>(registerInstance);
} else {
return {};
}
}

/**
Expand All @@ -1965,44 +1986,28 @@ inline constexpr int registerBlock(TRegisterInstance& registerInstance) {
* - TBlock -- the block class template with two template parameters
* - Tuple1 -- a std::tuple containing the types for the first template parameter of TBlock
* - Tuple2 -- a std::tuple containing the types for the second template parameter of TBlock
* - optionally more Tuples
*
* This function iterates over all combinations of the types in Tuple1 and Tuple2,
* instantiates TBlock with each combination, and registers the block with the registry.
*/
template<template<typename, typename> typename TBlock, typename Tuple1, typename Tuple2, typename TRegisterInstance>
inline constexpr int registerBlockTT(TRegisterInstance& registerInstance) {
auto addBlockType = [&]<typename Type1, typename Type2> {
using ThisBlock = TBlock<Type1, Type2>;
registerInstance.template addBlockType<ThisBlock>( //
vir::refl::class_name<ThisBlock>, vir::refl::type_name<Type1> + vir::fixed_string<",">() + vir::refl::type_name<Type2>);
};

std::apply(
[&]<typename... T1>(T1...) { // iterate over first type
std::apply(
[&]<typename... T2>(T2...) { // iterate over second type
(([&]<typename Type1>() { ((addBlockType.template operator()<Type1, T2>()), ...); }.template operator()<T1>()), ...);
},
Tuple2{});
},
Tuple1{});

template<template<typename...> typename TBlock, typename... Tuples>
inline constexpr int registerBlockTT(auto& registerInstance) {
meta::outer_product<meta::to_typelist<Tuples>...>::for_each([&]<typename Types>(std::size_t, Types*) { registerBlock<TBlock, typename Types::template apply<BlockParameters>>(registerInstance); });
return {};
}

template<template<typename, typename> typename TBlock, typename TBlockParameter0, typename... TBlockParameters>
inline int registerBlock(auto& registerInstance) {
using ThisBlock = TBlock<typename TBlockParameter0::template at<0>, typename TBlockParameter0::template at<1>>;
static_assert(meta::is_instantiation_of<TBlockParameter0, BlockParameters>);
static_assert(TBlockParameter0::size == 2);
registerInstance.template addBlockType<ThisBlock>(vir::refl::class_name<ThisBlock>, TBlockParameter0::toString());
if constexpr (sizeof...(TBlockParameters) != 0) {
return registerBlock<TBlock, TBlockParameters...>(registerInstance);
} else {
return {};
}
}

// FIXME: the following are inconsistent in how they specialize the template. Multiple types can be given, resulting in
// multiple specializations for each type. If the template requires more than one type then the types must be passed as
// a typelist (BlockParameters) instead. NTTP arguments, however, can only be given exactly as many as there are NTTPs
// in the template. Also the order is "messed up".
// Suggestion:
// template <auto X> struct Nttp { static constexpr value = X; };
// then use e.g.
// - BlockParameters<float, Nttp<Value>>
// - BlockParameters<float, Nttp<Value0>, double, Nttp<Value1>, Nttp<Value2>>
// Sadly, we can't remove any of the following overloads because we can't generalize the template template parameter
// (yet). And in principle, there's always another overload missing.
template<template<typename, auto> typename TBlock, auto Value0, typename... TBlockParameters, typename TRegisterInstance>
inline constexpr int registerBlock(TRegisterInstance& registerInstance) {
auto addBlockType = [&]<typename Type> {
Expand Down
41 changes: 41 additions & 0 deletions meta/include/gnuradio-4.0/meta/typelist.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,47 @@ struct concat_impl<A, B, C, D, More...> {
template<typename... Ts>
using concat = typename detail::concat_impl<Ts...>::type;

// outer_product ////////
namespace detail {
template<typename...>
struct outer_product_impl;

template<typename... As>
struct outer_product_impl<typelist<As...>> {
using type = typelist<As...>;
};

template<typename... Bs>
struct outer_product_impl<typelist<>, typelist<Bs...>> {
using type = typelist<>;
};

template<typename A0, typename... Bs>
struct outer_product_impl<typelist<A0>, typelist<Bs...>> {
using type = typelist<typelist<A0, Bs>...>;
};

template<typename A0, typename A1, typename... Bs>
struct outer_product_impl<typelist<A0, A1>, typelist<Bs...>> {
using type = typelist<typelist<A0, Bs>..., typelist<A1, Bs>...>;
};

template<typename A0, typename A1, typename A2, typename... As, typename... Bs>
struct outer_product_impl<typelist<A0, A1, A2, As...>, typelist<Bs...>> {
using tmp = typename outer_product_impl<typelist<As...>, typelist<Bs...>>::type;
using type = concat<typelist<typelist<A0, Bs>..., typelist<A1, Bs>..., typelist<A2, Bs>...>, tmp>;
};

template<typename A, typename B, typename... More>
requires(sizeof...(More) > 0)
struct outer_product_impl<A, B, More...> {
using type = typename outer_product_impl<typename outer_product_impl<A, B>::type, More...>::type;
};
} // namespace detail

template<typename... Ts>
using outer_product = typename detail::outer_product_impl<Ts...>::type;

// split_at, left_of, right_of ////////////////
namespace detail {
template<unsigned N>
Expand Down

0 comments on commit 8b6584b

Please sign in to comment.