Date: Sat, 13 Jul 2019 23:55:31 +0200
Hi all,
I would like to propose a very simple addition to the standard library in
order to simplify variadic parameter pack expansion, and before writing a
formal proposal I just wanted to share it in order to get feedback.
Typically, expanding indices sequences require writing a dedicated helper
wrapper. This is tedious and offers no value at all. A Search through the
Internet shows that similar approaches have been informally proposed, for
instance, in [stack overflow](
https://stackoverflow.com/questions/47210956/c17-multiple-parameter-pack-expansion
)
The proposal is about having a generic, reusable wrapper for expanding
integer sequences similar to the std::apply facility for expanding tuples,
considering the following:
- simplify the common case of expanding an index sequence with a provided
lambda
- expansion must be usable as literal values to enable template
instantiation (for instance, can be used to call std::get<index>(x)...)
- support for non-sequential index sequences, and integral types other
than std::size_t
- syntax similar to std::apply and std::visit
- the name could be just std::apply as a new overload, as this is
conceptually the same as expanding a tuple but for integral constants
The proposal is then:
/// Invoke the Callable object function with a sequence of indices
/// \param[in] function A callable object that will be invoked with
std::integral_constant arguments instantiated for each index in the \a
sequence integer_sequence
/// \param[in] sequence A std::integer_sequence to be expanded
/// \return The \a apply helper function returns whatever the provided
callable object returns
namespace std
{
template<typename F, typename T, T... indices>
constexpr decltype(auto) apply(F&& function, [[maybe_unused]]
std::integer_sequence<T, indices...> sequence)
{
return function(std::integral_constant<T, indices>{}...);
}
}
The idea of having arguments encoded as std::integral_constant allows for
using them not only as constexpr but also as template arguments.
This simple helper function could allow us to write, for instance:
/// performs the addition of two arrays of arithmetic types
template<typename T, std::size_t N>
auto operator +(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
-> std::enable_if_t<std::is_arithmetic_v<T>, std::array<T, N>>
{
return std::apply([&](auto... indices) { return std::array<T, N>{
(std::get<indices>(lhs) + std::get<indices>(rhs))... }; },
std::make_index_sequence<N>{});
}
int main(int argc, char** argv)
{
auto result = std::array{1, 2, 3} + std::array{5, 4, 3};
apply([&](auto... indices) { (std::cout << ... << result[indices]); },
std::make_index_sequence<3>{});
}
Even the current std::apply function for tuples could be rewriten on top of
this proposal.
No language changes are required. It can be implemented on top of C++17 and
C++14.
Waiting for your feedback,
Jordi
I would like to propose a very simple addition to the standard library in
order to simplify variadic parameter pack expansion, and before writing a
formal proposal I just wanted to share it in order to get feedback.
Typically, expanding indices sequences require writing a dedicated helper
wrapper. This is tedious and offers no value at all. A Search through the
Internet shows that similar approaches have been informally proposed, for
instance, in [stack overflow](
https://stackoverflow.com/questions/47210956/c17-multiple-parameter-pack-expansion
)
The proposal is about having a generic, reusable wrapper for expanding
integer sequences similar to the std::apply facility for expanding tuples,
considering the following:
- simplify the common case of expanding an index sequence with a provided
lambda
- expansion must be usable as literal values to enable template
instantiation (for instance, can be used to call std::get<index>(x)...)
- support for non-sequential index sequences, and integral types other
than std::size_t
- syntax similar to std::apply and std::visit
- the name could be just std::apply as a new overload, as this is
conceptually the same as expanding a tuple but for integral constants
The proposal is then:
/// Invoke the Callable object function with a sequence of indices
/// \param[in] function A callable object that will be invoked with
std::integral_constant arguments instantiated for each index in the \a
sequence integer_sequence
/// \param[in] sequence A std::integer_sequence to be expanded
/// \return The \a apply helper function returns whatever the provided
callable object returns
namespace std
{
template<typename F, typename T, T... indices>
constexpr decltype(auto) apply(F&& function, [[maybe_unused]]
std::integer_sequence<T, indices...> sequence)
{
return function(std::integral_constant<T, indices>{}...);
}
}
The idea of having arguments encoded as std::integral_constant allows for
using them not only as constexpr but also as template arguments.
This simple helper function could allow us to write, for instance:
/// performs the addition of two arrays of arithmetic types
template<typename T, std::size_t N>
auto operator +(const std::array<T, N>& lhs, const std::array<T, N>& rhs)
-> std::enable_if_t<std::is_arithmetic_v<T>, std::array<T, N>>
{
return std::apply([&](auto... indices) { return std::array<T, N>{
(std::get<indices>(lhs) + std::get<indices>(rhs))... }; },
std::make_index_sequence<N>{});
}
int main(int argc, char** argv)
{
auto result = std::array{1, 2, 3} + std::array{5, 4, 3};
apply([&](auto... indices) { (std::cout << ... << result[indices]); },
std::make_index_sequence<3>{});
}
Even the current std::apply function for tuples could be rewriten on top of
this proposal.
No language changes are required. It can be implemented on top of C++17 and
C++14.
Waiting for your feedback,
Jordi
Received on 2019-07-13 16:57:38