Is is possible to make the first template argument of a function template default to the second one if the first one is not specified?
Here is a small example:
#include <algorithm>
#include <list>
#include <vector>
template <typename ContainerOut, typename ContainerIn>
ContainerOut KeepNegatives(const ContainerIn& xs)
{
ContainerOut result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x){ return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
int main()
{
typedef std::vector<int> IntVector;
typedef std::list<int> IntList;
IntVector intVector = { 1, -2, -3, 4 };
IntList intList = { 1, -2, -3, 4 };
auto intVec2 = KeepNegatives<IntVector>(intList);
auto intList2 = KeepNegatives<IntList>(intVector);
auto intVec3 = KeepNegatives<IntVector>(intVector);
}
This works, but what I want is, that the type of the return value of KeepNegatives (i.e. ContainerOut) is the same as the type of the input value (i.e. ContainerIn) in case ContainerOut is not specified. So that the following line of code would compile (right now it does not) and return an IntVector.
auto intVec4 = KeepNegatives(intVector);
You could simply add an overload for this special case:
template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs)
{
return KeepNegatives<ContainerIn, ContainerIn>(xs);
}
This, however, can cause an ambiguity in your intVec3 case. Here's one way around it:
#include <algorithm>
#include <list>
#include <vector>
template <typename ContainerOut, typename ContainerIn,
typename = std::enable_if_t<!std::is_same<ContainerOut, ContainerIn>::value>>
ContainerOut KeepNegatives(const ContainerIn& xs)
{
ContainerOut result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x){ return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs)
{
return KeepNegatives<ContainerIn, ContainerIn, void>(xs);
}
int main()
{
typedef std::vector<int> IntVector;
typedef std::list<int> IntList;
IntVector intVector = { 1, -2, -3, 4 };
IntList intList = { 1, -2, -3, 4 };
auto intVec2 = KeepNegatives<IntVector>(intList);
auto intList2 = KeepNegatives<IntList>(intVector);
auto intVec3 = KeepNegatives<IntVector>(intVector);
auto intVec4 = KeepNegatives(intVector);
}
Live
A better approach, taken by, e.g., std::experimental::make_array, is to not use ContainerOut as the return type directly; this allows you to specify a tag type as the default (void is a simple choice), and then compute the return type.
template <typename ContainerOut = void, typename ContainerIn,
typename ret_t = std::conditional_t<std::is_void<ContainerOut>{},
ContainerIn, ContainerOut>>
ret_t KeepNegatives(const ContainerIn& xs){
// ...
}
You could combine SFINAE with an overload as below:
template <typename ContainerOut, typename ContainerIn>
std::enable_if_t<!std::is_same<ContainerOut, ContainerIn>::value, ContainerOut>
KeepNegatives(const ContainerIn& xs) {
ContainerOut result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x) { return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
template <typename ContainerIn>
ContainerIn
KeepNegatives(const ContainerIn& xs) {
ContainerIn result;
auto itOut = std::inserter(result, std::end(result));
auto isNegative = [](auto x) { return x < 0; };
std::copy_if(std::begin(xs), std::end(xs), itOut, isNegative);
return result;
}
and in main:
auto intVec2 = KeepNegatives<IntVector>(intList);
auto intList2 = KeepNegatives<IntList>(intVector);
auto intVec3 = KeepNegatives(intVector);
LIVE DEMO
Providing just a simple overload as:
template <typename ContainerIn>
ContainerIn KeepNegatives(const ContainerIn& xs) {...}
You'll get an ambiguous call to overloaded function, if the user provides explicitly the same template argument as:
auto intVec3 = KeepNegatives<IntVector>(intVector);
Related
Tuple foreach is relatively simple using either recursion or std::apply:
#include <cstring>
#include <iostream>
#include <tuple>
#include <utility>
template<typename F, typename T>
auto foreach_apply(F&& f, T &&t) {
return std::apply([&f](auto&&... elements) {
return (f(std::forward<decltype(elements)>(elements)) || ...);
}, std::forward<T>(t));
}
template <std::size_t I=0, typename F, typename... Ts>
void foreach_recurse(F&& f, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
return;
} else {
f(std::get<I>(t));
find<I+1>(f, t);
}
}
int main() {
//auto a = std::tuple(true, true, false, false, true);
auto a = std::tuple("one", "two", "three", "four", "five");
//auto b = foreach_recurse([](auto& element) -> bool {
auto b = foreach_apply([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
}
Indexing is only slightly trickier:
template <std::size_t I=0, typename F, typename... Ts>
size_t index_recurse(F&& f, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
return -1;
} else {
auto e = std::get<I>(t);
if (f(e))
return I;
return index_recurse<I+1>(f, t);
}
}
template <std::size_t I=0, typename F, typename... Ts>
bool index_recurse_2(F&& f, std::tuple<Ts...> t, size_t* i) {
if constexpr (I == sizeof...(Ts)) {
return false;
} else {
auto e = std::get<I>(t);
if (f(e)) {
*i = I;
return true;
}
return index_recurse_2<I+1>(f, t, i);
}
}
template<typename F, typename T>
auto index_apply(F&& f, T &&t, size_t* ix) {
return std::apply([&f,ix] (auto&&... elements) {
return [&f,ix]<std::size_t... I>(std::index_sequence<I...>, auto&&... elements) {
auto fi = [&f,ix](auto i, auto&& element) {
auto r = f(std::forward<decltype(element)>(element));
if (r)
*ix = i;
return r;
};
return (fi(I, std::forward<decltype(elements)>(elements)) || ...);
}
( std::make_index_sequence<sizeof...(elements)>()
, std::forward<decltype(elements)>(elements)...
);
}, std::forward<T>(t));
}
int main() {
/*
auto a = std::tuple("one", "two", "three", "four", "five");
auto b = index_recurse([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
*/
/*
auto a = std::tuple("one", "two", "three", "four", "five");
size_t b;
auto c = index_recurse_2([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a, &b);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
*/
/*
auto a = std::tuple("one", "two", "three", "four", "five");
size_t b;
auto c = index_apply([](auto& element) -> bool {
std::cout << element << std::endl;
if (!strcmp("three", element))
return true;
else
return false;
}, a, &b);
std::cout << "Done" << std::endl;
std::cout << b << std::endl << std::endl;
*/
}
Now, get the value rather than the index. The index_* functions show that this is possible. We take a compile-time value (tuple) and a set of compile-time functions (unrolled index functions), apply a runtime function that matches an input, and get a runtime value that depends on the compile-time values. This is what the find_* functions attempt to do.
Given a tuple T of length N, find_broken_1 unrolls to N functions of type (i:0-N, T) where each function either returns the i-th function, or returns from the next in the sequence. That means the return type of the i-th function must match all the previous return types. So this recursive approach can't work.
template <std::size_t I=0, typename F, typename... Ts>
auto* find_broken_1(F&& f, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
// What type? Can this be fixed?
return (const char**)&"<>";
//return (void*)nullptr;
//return ((decltype(std::get<I-1>)::type)*)nullptr;
} else {
auto& e = std::get<I>(t);
if (f(e))
return &e;
std::cout << e << std::endl;
return find_broken_1<I+1>(f, t);
}
}
Here index is not known at compile-time so std::get won't compile. It would be nice if C++ would template for every possible index so this would work during runtime, just like C++ already unrolls every possible index function.
template <typename F, typename T>
auto find_broken_2(F&& f, T&& t, bool* b) {
const size_t i = index_recurse(f, t);
if (i >= 0) {
*b = true;
return std::get<i>(t);
}
else {
*b = false;
//return nullptr;
}
}
We know we can generate a runtime value from a compile-time value. If the transform is reversible and the bounds are known, we should be able to lookup a compile-time value from a runtime value. How can we trick the compiler into doing so? Then integrate this into the index_* functions to avoid double iteration.
Moving something into a type forces it to become a compile-time something. Unfortunately this unrolls to a different return type for each generated function, and since the returns are chained, generates a compile error. Once again, the recursive approach fails.
I'm just trying a bunch of different ideas, none working:
template<bool value, typename ...>
struct bool_type : std::integral_constant<bool , value> {};
//std::integral_constant<int, I> run_to_compile(size_t i, std::tuple<Ts...> t) {
template <std::size_t I=0, typename... Ts>
auto run_to_compile(size_t i, std::tuple<Ts...> t) {
if constexpr (I == sizeof...(Ts)) {
return std::integral_constant<int, I-1>();
// replace with static_assert... nope
//static_assert(bool_type<false, Ts...>::value, "wrong");
//return I-1;
}
else {
//if (i == I) {
if (I > 2) {
//return;
return std::integral_constant<int, I>();
//return I;
}
return run_to_compile<I+1>(i, t);
}
}
template<int value, typename ...>
struct int_type : std::integral_constant<int , value> {};
template <int I=0, typename T>
constexpr auto run_to_compile_2(int i) {
return int_type<i, T>();
}
template <typename F, typename T>
auto find_broken_3(F&& f, T&& t, bool* b) {
const size_t ir = index_recurse(f, t);
// nope
//constexpr decltype(t) temp = {};
// nope, again (really?)
//auto ic = std::integral_constant<int, ir>();
//constexpr auto ic = run_to_compile(0, temp);
//const auto ic = run_to_compile(ir, t);
//const auto ic = r2c_2<const int>(ir);
run_to_compile_2<2, int>(2);
const auto ic = 2;
if (ir >= 0) {
*b = true;
return std::get<ic>(t);
}
else {
*b = false;
//return nullptr;
}
}
How can I fix this?
Return type cannot depend of runtime. So a possibility to unify the return type is std::variant:
template <typename F, typename... Ts>
auto find_in_tuple(F&& f, std::tuple<Ts...> t)
{
std::optional<std::variant<Ts...>> res;
std::apply([&](auto&&... args){
auto lambda = [&](auto&& arg){
if (!res && f(arg))
res = arg;
};
(lambda(args), ...);
}, t);
return res;
}
Demo
Let's say I have a function
template<typename retScalar, typename... scalars>
retScalar func_scalar(scalars... items);
declared somewhere.
I now want a "array" version of this function
template<typename retScalar, typename... scalars>
std::array<retScalar, LEN> func_vec(std::tuple<std::array<scalars, LEN>...> vecs) {
std::array<retScalar, LEN> res; // initialized somehow
for (int i = 0; i < LEN; ++i) {
res[i] = func_scalar(/* ?? */);
}
return res;
}
I searched a lot and don't see any correct way to do that.
Thanks to another anwser, I found this solution, which I find pretty cool and probably useful for other people
#include <cstdio>
#include <tuple>
template<typename T>
struct myvect {
T data[4];
};
template<typename... Ts>
struct helper {
template<std::size_t... I>
static auto inner(int index, std::tuple<myvect<Ts>...> vects, std::index_sequence<I...>)
{
return std::make_tuple(std::get<I>(vects).data[index]...);
}
};
template<typename... Ts>
auto elements(int index, std::tuple<myvect<Ts>...> vectors)
{
return helper<Ts...>::template inner(index, vectors, std::index_sequence_for<Ts...>{});
}
template<typename retS, typename... args>
struct vectorize {
template<retS(*funcptr)(args...)>
static myvect<retS> func_vect(std::tuple<myvect<args>...> v) {
myvect<retS> res;
for (int i = 0; i < 4; ++i) {
res.data[i] = std::apply(funcptr, elements(i, v));
}
return res;
}
};
int test(int a, int b) {
return a + b;
}
int main() {
myvect<int> a { 1, 2, 3, 4 };
myvect<int> b { 2, 4, 6, 8 };
auto added = vectorize<int, int, int>::func_vect<&test>(std::make_tuple(a, b));
for (int i = 0; i < 4; ++i) {
printf("%d\n", added.data[i]);
}
}
I have the following code to generate tuples of adjacent pairs in a range. This works for bidirectional ranges but not for forward only ranged.
template <typename Range>
// Returns a range of adjacent pairs of the input range
auto make_adjacent_range(Range const & r) -> decltype(boost::combine(
boost::make_iterator_range(boost::begin(r), boost::prior(boost::end(r))),
boost::make_iterator_range(boost::next(boost::begin(r)), boost::end(r))))
{
return boost::combine(
boost::make_iterator_range(boost::begin(r), boost::prior(boost::end(r))),
boost::make_iterator_range(boost::next(boost::begin(r)), boost::end(r)));
}
boost::prior is not accepted with a forward only range. Is there an equally elegant solution that will work with forward ranges?
boost::combine
Not really elegant, but you can just write a adjacent_iterator type.
It's relatively tricky to get it to work for InputIterator, as you have to dereference before each increment
template <typename InputIterator>
class adjacent_iterator
{
public:
using element_type = std::iterator_traits<InputIterator>::value_type;
using value_type = std::pair<element_type, element_type>;
using category = std::iterator_traits<InputIterator>::category;
// all the other typedefs
adjacent_iterator& operator++()
{
element.first = element.second;
if (needs_deref) element.second = *it;
++it;
needs_deref = true;
return *this;
}
reference operator*()
{
element.second = *it;
needs_deref = false;
return element;
}
// all the other members
friend bool operator==(adjacent_iterator lhs, adjacent_iterator rhs)
{
// only check the iterator
return lhs.it == rhs.it;
}
private:
adjacent_iterator(element_type first, InputIterator second)
: element(first, {}), it(second), needs_deref(true) {}
adjacent_iterator(InputIterator end)
: it(end) {}
InputIterator it;
value_type element;
bool needs_deref;
// not sure how to declare this friendship
template <typename Range>
friend auto make_adjacent_range(Range const & r)
{
auto begin = boost::begin(r);
auto end = boost::end(r);
using IT = decltype(boost::begin(r));
auto elem = *begin++;
auto b = adjacent_iterator<IT>(elem, begin);
auto e = adjacent_iterator<IT>(end);
return boost::make_iterator_range(b, e);
}
};
This works, but could be quite inefficient depending on the iterator types:
template <typename Range>
auto make_adjacent_range(Range const & r) {
auto n = boost::size(r);
auto b = boost::begin(r);
auto r1 = boost::make_iterator_range(b, boost::next(b, n-1));
auto r2 = r1;
r2.advance_begin(1);
r2.advance_end(1);
return boost::combine(r1, r2);
}
Live On Coliru
#include <boost/range.hpp>
#include <boost/range/combine.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <iostream>
#include <forward_list>
template <typename Range>
auto make_adjacent_range(Range const & r) {
auto n = boost::size(r);
auto b = boost::begin(r);
auto r1 = boost::make_iterator_range(b, boost::next(b, n-1));
auto r2 = r1;
r2.advance_begin(1);
r2.advance_end(1);
return boost::combine(r1, r2);
}
int main() {
std::forward_list<int> v{1,2,3,4};
for (auto p : make_adjacent_range(v))
std::cout << p.get<0>() << " " << p.get<1>() << "\n";
}
Prints
1 2
2 3
3 4
Perhaps it would be nicer to make an iterator adaptor.
Is it a better idea to use boost::join to access and change the values of different arrays?
I have defined a member array inside class element.
class element
{
public:
element();
int* get_arr();
private:
int m_arr[4];
}
At different place, I'm accessing these arrays and joined together using boost::join and changing the array values.
//std::vector<element> elem;
auto temp1 = boost::join(elem[0].get_arr(),elem[1].get_arr());
auto joined_arr = boost::join(temp1,elem[2].get_arr());
//now going to change the values of the sub array
for(auto& it:joined_arr)
{
it+= sample[i];
i++;
}
Is this a good idea to modify the values of array in the class as above?
In your code you probably want to join the 4-elements arrays. To do that change the signature of get_arr to:
typedef int array[4];
array& get_arr() { return m_arr; }
So that the array size does not get lost.
Performance-wise there is a non-zero cost for accessing elements through the joined view. A double for loop is going to be most efficient, and easily readable too, e.g.:
for(auto& e : elem)
for(auto& a : e.get_arr())
a += sample[i++];
boost::join returns a more complicated type every composition step. At some point you might exceed the compiler's limits on inlining so that you're going to have a runtime cost¹.
Thinking outside the box, it really looks like you are creating a buffer abstraction that allows you to do scatter/gather like IO with few allocations.
As it happens, Boost Asio has nice abstractions for this², and you could use that: http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/MutableBufferSequence.html
As I found out in an earlier iteration of this answer code that abstraction sadly only works for buffers accessed through native char-type elements. That was no good.
So, in this rewrite I present a similar abstraction, which consists of nothing than an "hierarchical iterator" that knows how to iterate a sequence of "buffers" (in this implementation, any range will do).
You can choose to operate on a sequence of ranges directly, e.g.:
std::vector<element> seq(3); // tie 3 elements together as buffer sequence
element& b = seq[1];
Or, without any further change, by reference:
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
The C++ version presented at the bottom demonstrates this approach Live On Coliru
The Iterator Implementation
I've used Boost Range and Boost Iterator:
template <typename Seq,
typename WR = typename Seq::value_type,
typename R = typename detail::unwrap<WR>::type,
typename V = typename boost::range_value<R>::type
>
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
using OuterIt = typename boost::range_iterator<Seq>::type;
using InnerIt = typename boost::range_iterator<R>::type;
// state
Seq& _seq;
OuterIt _ocur, _oend;
InnerIt _icur, _iend;
static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
static sequence_iterator end(Seq& seq) { return {seq, boost::end(seq), boost::end(seq)}; }
// the 3 facade operations
bool equal(sequence_iterator const& rhs) const {
return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
|| (std::addressof(_seq) == std::addressof(rhs._seq) &&
_ocur == rhs._ocur && _oend == rhs._oend &&
_icur == rhs._icur && _iend == rhs._iend);
}
void increment() {
if (++_icur == _iend) {
++_ocur;
setup();
}
}
V& dereference() const {
assert(_ocur != _oend);
assert(_icur != _iend);
return *_icur;
}
private:
void setup() { // to be called after entering a new sub-range in the sequence
while (_ocur != _oend) {
_icur = boost::begin(detail::get(*_ocur));
_iend = boost::end(detail::get(*_ocur));
if (_icur != _iend)
break;
++_ocur; // skid over, this enables simple increment() logic
}
}
sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
: _seq(seq), _ocur(cur), _oend(end) { setup(); }
};
That's basically the same kind of iterator as boost::asio::buffers_iterator but it doesn't assume an element type. Now, creating sequence_iterators for any sequence of ranges is as simple as:
template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq) { return sequence_iterator<Seq>::end(seq); }
Implementing Your Test Program
Live On Coliru
// DEMO
struct element {
int peek_first() const { return m_arr[0]; }
auto begin() const { return std::begin(m_arr); }
auto end() const { return std::end(m_arr); }
auto begin() { return std::begin(m_arr); }
auto end() { return std::end(m_arr); }
private:
int m_arr[4] { };
};
namespace boost { // range adapt
template <> struct range_iterator<element> { using type = int*; };
// not used, but for completeness:
template <> struct range_iterator<element const> { using type = int const*; };
template <> struct range_const_iterator<element> : range_iterator<element const> {};
}
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
auto ib = boost::begin(input), ie = boost::end(input);
auto ob = boost::begin(output), oe = boost::end(output);
size_t n = 0;
for (;ib!=ie && ob!=oe; ++n) {
op(*ob++, *ib++);
}
return n;
}
int main() {
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
//// Also supported, container of range objects directly:
// std::list<element> seq(3); // tie 3 elements together as buffer sequence
// element& b = seq[1];
std::vector<int> const samples {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32
};
using boost::make_iterator_range;
auto input = make_iterator_range(samples);
auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));
while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
input.advance_begin(n);
}
}
Prints
Copied 12 samples, b starts with 5
Copied 12 samples, b starts with 22
Copied 8 samples, b starts with 51
Full Listing, C++11 Compatible
Live On Coliru
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional> // std::reference_wrapper
namespace detail {
template<typename T> constexpr T& get(T &t) { return t; }
template<typename T> constexpr T const& get(T const &t) { return t; }
template<typename T> constexpr T& get(std::reference_wrapper<T> rt) { return rt; }
template <typename T> struct unwrap { using type = T; };
template <typename T> struct unwrap<std::reference_wrapper<T> > { using type = T; };
}
template <typename Seq,
typename WR = typename Seq::value_type,
typename R = typename detail::unwrap<WR>::type,
typename V = typename boost::range_value<R>::type
>
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
using OuterIt = typename boost::range_iterator<Seq>::type;
using InnerIt = typename boost::range_iterator<R>::type;
// state
Seq& _seq;
OuterIt _ocur, _oend;
InnerIt _icur, _iend;
static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
static sequence_iterator end(Seq& seq) { return {seq, boost::end(seq), boost::end(seq)}; }
// the 3 facade operations
bool equal(sequence_iterator const& rhs) const {
return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
|| (std::addressof(_seq) == std::addressof(rhs._seq) &&
_ocur == rhs._ocur && _oend == rhs._oend &&
_icur == rhs._icur && _iend == rhs._iend);
}
void increment() {
if (++_icur == _iend) {
++_ocur;
setup();
}
}
V& dereference() const {
assert(_ocur != _oend);
assert(_icur != _iend);
return *_icur;
}
private:
void setup() { // to be called after entering a new sub-range in the sequence
while (_ocur != _oend) {
_icur = boost::begin(detail::get(*_ocur));
_iend = boost::end(detail::get(*_ocur));
if (_icur != _iend)
break;
++_ocur; // skid over, this enables simple increment() logic
}
}
sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
: _seq(seq), _ocur(cur), _oend(end) { setup(); }
};
template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq) { return sequence_iterator<Seq>::end(seq); }
// DEMO
struct element {
int peek_first() const { return m_arr[0]; }
auto begin() const { return std::begin(m_arr); }
auto end() const { return std::end(m_arr); }
auto begin() { return std::begin(m_arr); }
auto end() { return std::end(m_arr); }
private:
int m_arr[4] { };
};
namespace boost { // range adapt
template <> struct range_iterator<element> { using type = int*; };
// not used, but for completeness:
template <> struct range_iterator<element const> { using type = int const*; };
template <> struct range_const_iterator<element> : range_iterator<element const> {};
}
#include <algorithm>
#include <iostream>
#include <vector>
template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
auto ib = boost::begin(input), ie = boost::end(input);
auto ob = boost::begin(output), oe = boost::end(output);
size_t n = 0;
for (;ib!=ie && ob!=oe; ++n) {
op(*ob++, *ib++);
}
return n;
}
int main() {
element a, b, c;
std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence
//// Also supported, container of range objects directly:
// std::list<element> seq(3); // tie 3 elements together as buffer sequence
// element& b = seq[1];
std::vector<int> const samples {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32
};
using boost::make_iterator_range;
auto input = make_iterator_range(samples);
auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));
while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
input.advance_begin(n);
}
}
¹ I'm ignoring the compile-time cost and the lurking dangers with the pattern of auto x = complicated_range_composition when that complicated range composition contains references to temporaries: this is a frequent source of UB bugs
² which have been adopted by various other libraries, like Boost Beast, Boost Process and seem to have found their way into the Networking TS for C++20: Header <experimental/buffer> synopsis (PDF)
I have the following snippet of code:
#include <type_traits>
#include <limits>
#include <initializer_list>
#include <cassert>
template <typename F, typename... FIn>
auto min_on(F f, const FIn&... v) -> typename std::common_type<FIn...>::type
{
using rettype = typename std::common_type<FIn...>::type;
rettype result = std::numeric_limits<rettype>::max();
(void)std::initializer_list<int>{((f(v) < result) ? (result = static_cast<rettype>(v), 0) : 0)...};
return result;
}
int main()
{
auto mod2 = [](int a)
{
return a % 2;
};
assert(min_on(mod2, 2) == 2); // PASSES as it should
assert(min_on(mod2, 3) == 3); // PASSES as it should
assert(min_on(mod2, 2, 3) == 3); // PASSES but shouldn't - should be 2
assert(min_on(mod2, 2, 3) == 2); // FAILS but shouldn't - should be 2
}
The idea behind template function min_on is that it should return the parameter x from list of parameters passed to it v so that it gives the smallest values for expression f(v).
The problem that I have observed is that somehow the order of parameters inside the std::initializer_list is important so the the code above will fail whereas this code:
assert(min_on(mod2, 3, 2) == 2);
will work. What might be wrong in here?
Your function sets result to v if f(v) < result. With mod2 as f, f(v) will only ever result in a 0, 1 or a -1. Which means that if all of your values are greater than 1, result will be set to the last v which was tested, because f(v) will always be less than result. Try putting a negative number in the middle of a bunch of positive numbers, and the negative number will always be the result, no matter where you place it.
assert(min_on(mod2, 2, 3, 4, -3, 7, 6, 5) == -3);
Perhaps you want this instead:
std::initializer_list<int>{((f(v) < f(result)) ? (result = static_cast<rettype>(v), 0) : 0)...};
The difference is I am testing f(v) < f(result), instead of f(v) < result. Although, the function is still not correct generally because it assumes that f(std::numeric_limits<rettype>::max()) is the max possible value. In the case of mod2 it works. But with something like this:
[](int a) { return -a; }
it would clearly be wrong. So perhaps you could instead require a first argument:
template <typename F, typename FirstT, typename... FIn>
auto min_on(F f, const FirstT& first, const FIn&... v)
-> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
rettype result = first;
(void)std::initializer_list<int>{((f(v) < f(result)) ? (result = static_cast<rettype>(v), 0) : 0)...};
return result;
}
Or, if you're want to avoid unnecessary calls to f:
template <typename F, typename FirstT, typename... FIn>
auto min_on(F f, const FirstT& first, const FIn&... v)
-> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
rettype result = first;
auto result_trans = f(result);
auto v_trans = result_trans;
(void)std::initializer_list<int>{(
(v_trans = f(v), v_trans < result_trans)
? (result = static_cast<rettype>(v), result_trans = v_trans, 0) : 0)...};
return result;
}