In the test case below, I use boost::combine to iterate on output of a function getPoints().
Expected Output
I expect (1, 2, 3) printed 6 times; since I effectively zip two lists -
([point, point, point], [point, point, point]).
Actual Output
The output is surprising to me, and wrong. The first two lines are off suggesting memory corruption?
(0, 0, 3) // <-- wrong!
(52246144, 0, 3) // <-- wrong! memory corruption?
(1, 2, 3)
(1, 2, 3)
(1, 2, 3)
(1, 2, 3)
This can also be verified online here, http://cpp.sh/622h4.
Is this a bug?
Code below -
#include <iostream>
#include <vector>
#include <boost/range/combine.hpp>
struct Point {
int x, y, z;
};
const std::vector<Point> getPoints() {
// There is only one Point in the entire code, which is (1, 2, 3).
const Point point = {1, 2, 3};
// Return a vectore of 3 copies of the point (1, 2, 3).
return {point, point, point};
}
int main() {
// Zip over two copies of 3-tuples of {1, 2, 3}.
for (const auto& zipped : boost::combine(getPoints(), getPoints())) {
auto p1 = zipped.get<0>();
auto p2 = zipped.get<1>();
// Expected output is (1, 2, 3), six times.
std::cout << "(" << p1.x << ", " << p1.y << ", " << p1.z << ")" << std::endl;
std::cout << "(" << p2.x << ", " << p2.y << ", " << p2.z << ")" << std::endl;
}
return 0;
}
You have undefined behavior here as you access a dangling reference. This can be fixed by
const auto points1 = getPoints();
const auto points2 = getPoints();
for (const auto& zipped : boost::combine(points1, points2)) {
// ...
}
Rvalue references are always problematic when dealing with range libraries. Obviously, a range algorithm like boost::combine doesn't copy the argument. And it creates a new proxy range object, which makes it impossible to extend the lifetime of the temporary range passed in.
Contrary, a range-based for loop for(const auto& item: getPoints()) {...} expands to
{
auto && __range = getPoints();
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
where the lifetime of getPoints() is extended by binding it to an rvalue reference. Imagine a function template combine as
template<class Rng>
auto combine(Rng&& rng) {
auto && == range; // Nice try but doesn't help
// ...
return someProxyRange;
}
This function template can't do anything about extending the lifetime of rng, as it acts in a different scope than rng, which comes from the client side. In a range based for loop, this is different. The scope of the temporary (e.g. getPoints()) and the forwarding reference auto&& __range are at the same scope, hence the lifetime can be extended.
Related
I have algorithm that uses iterators, but there is a problem with transforming values, when we need more than single source value.
All transform iterators just get some one arg and transforms it. (see similar question from the past)
Code example:
template<typename ForwardIt>
double some_algorithm(ForwardIt begin, ForwardIt end) {
double result = 0;
for (auto it = begin; it != end; ++it) {
double t = *it;
/*
do some calculations..
*/
result += t;
}
return result;
}
int main() {
{
std::vector<double> distances{ 1, 2, 3, 4 };
double t = some_algorithm(distances.begin(), distances.end());
std::cout << t << std::endl;
/* works great */
}
{
/* lets now work with vector of points.. */
std::vector<double> points{ 1, 2, 4, 7, 11 };
/* convert to distances.. */
std::vector<double> distances;
distances.resize(points.size() - 1);
for (size_t i = 0; i + 1 < points.size(); ++i)
distances[i] = points[i + 1] - points[i];
/* invoke algorithm */
double t = some_algorithm(distances.begin(), distances.end());
std::cout << t << std::endl;
}
}
Is there a way (especialy using std) to create such an iterator wrapper to avoid explicitly generating distances value?
It could be fine to perform something like this:
template<typename BaseIterator, typename TransformOperator>
struct GenericTransformIterator {
GenericTransformIterator(BaseIterator it, TransformOperator op) : it(it), op(op) {}
auto operator*() {
return op(it);
}
GenericTransformIterator& operator++() {
++it;
return *this;
}
BaseIterator it;
TransformOperator op;
friend bool operator!=(GenericTransformIterator a, GenericTransformIterator b) {
return a.it != b.it;
}
};
and use like:
{
/* lets now work with vector of points.. */
std::vector<double> points{ 1, 2, 4, 7, 11 };
/* use generic transform iterator.. */
/* invoke algorithm */
auto distance_op = [](auto it) {
auto next_it = it;
++next_it;
return *next_it - *it;
};
double t = some_algorithm(
generic_transform_iterator(points.begin(), distance_op),
generic_transform_iterator(points.end() -1 , distance_op));
std::cout << t << std::endl;
}
So general idea is that transform function is not invoked on underlying object, but on iterator (or at least has some index value, then lambda can capture whole container and access via index).
I used to use boost which has lot of various iterator wrapping class.
But since cpp20 and ranges I'm curious if there is a way to use something existing from std:: rather than writing own wrappers.
With C++23, use std::views::pairwise.
In the meantime, you can use iota_view. Here's a solution which will work with any bidirectional iterators (e.g. points could be a std::list):
auto distances =
std::views::iota(points.cbegin(), std::prev(points.cend()))
| std::views::transform([](auto const &it) { return *std::next(it) - *it; });
This can also be made to work with any forward iterators. Example:
std::forward_list<double> points{1, 2, 4, 7, 11};
auto distances =
std::views::iota(points.cbegin())
| std::views::take_while([end = points.cend()](auto const &it) { return std::next(it) != end; })
| std::views::transform([](auto const &it) { return *std::next(it) - *it; })
| std::views::common;
Note that both of these snippets have undefined behaviour if points is empty.
I'm not sure this addresses your problem (let me know if it doesn't and I'll remove the answer), but you may be able to achieve that with ranges (unfortunately, not with standard ranges yet, but Eric Niebler's range-v3).
The code below:
groups the points vector in pairs,
calculates the difference between the second and the first element of each pair, and then
sums all those differences up.
[Demo]
auto t{ accumulate(
points | views::sliding(2) | views::transform([](const auto& v) { return v[1] - v[0]; }),
0.0
)};
I am kinda confused and think I get something wrong but I really fail to see it.
I have a boost::geometry::index::rtree that stores 2D boxes of 2D geographic points. Now I try to check if a new box I add to that rtree overlaps with any box already in that rtree. And this check somehow fails for one test and I really don't get why because I do not believe the error is in the rtree/overlaps implementation.
My code is the following (in Visual Studio test environment):
using CoordinateSystem = boost::geometry::cs::geographic<boost::geometry::degree>;
using Point = boost::geometry::model::point<double, 2, CoordinateSystem>;
using RTreeBox = boost::geometry::model::box<Point>;
using RTreeEntry = std::pair<RTreeBox, uint64_t>;
constexpr static auto kRTreeMaxElementsPerNode = 4;
using RTreeAlgorithm = boost::geometry::index::rstar<kRTreeMaxElementsPerNode>;
using RTree = boost::geometry::index::rtree<RTreeEntry, RTreeAlgorithm>;
bool TestAddTreeEntry(RTree& tree, uint64_t index, RTreeBox box)
{
if (!boost::geometry::is_valid(box)) {
boost::geometry::correct(box);
}
std::vector<RTreeEntry> query_results;
tree.query(boost::geometry::index::overlaps(box), std::back_inserter(query_results));
if (query_results.size() > 0)
{
return false;
}
tree.insert(std::make_pair(box, index));
return true;
}
TEST_METHOD(test_rtree_mapping) {
RTree tree;
Assert::IsTrue(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 1 }, { 3, 3 })));
Assert::IsTrue(TestAddTreeEntry(tree, 1, RTreeBox({ 4, 1 }, { 9, 5 })));
Assert::IsTrue(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 4 }, { 2, 9 })));
Assert::IsFalse(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 2.75 }, { 2, 9 })));
Assert::IsFalse(TestAddTreeEntry(tree, 1, RTreeBox({ 1, 4 }, { 3.5, 9 })));
}
The first Assert::IsFalse works - but also unexpectedly only overlaps with the first box ({1, 1}, {3, 3}) and not with the third one ({1, 4}, {2, 9}). The second Assert::IsFalse does not work because the entry is successfully added.
Anyone knows a reason behind this? Has this something to do with geographic coordinates that I do not understand yet?
Kind regards
Overlaps doesn't do what you expect. It's too strict. The docs state:
The function overlaps implements function Overlaps from the OGC Simple Feature Specification.
The OGC document contains:
That's admittedly hard to parse, but that's the nature of technical specifications. Luckily, intersects is much simpler:
a.Intersects(b) ⇔ ! a.Disjoint(b)
DEMO
I created a test program that
allows you to override the RELATION (e.g. bgi::intersects, bgi::overlaps, !bgi::disjoint)
also writes a SVG vizualtion of the shapes involved.
Live On Compiler Explorer
#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <fstream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
namespace bgi = bg::index;
using CS = bg::cs::geographic<bg::degree>;
using Point = bg::model::point<double, 2, CS>;
using Box = bg::model::box<Point>;
using Tree = std::pair<Box, uint64_t>;
constexpr static auto kRTreeMaxElementsPerNode = 4;
using RTreeAlgorithm = bgi::rstar<kRTreeMaxElementsPerNode>;
using RTree = bgi::rtree<Tree, RTreeAlgorithm>;
#ifndef RELATION
#define RELATION bgi::intersects
#endif
#define RELATION_STR BOOST_PP_STRINGIZE(RELATION)
bool add(RTree& tree, uint64_t index, Box box) {
if (std::string reason; !bg::is_valid(box, reason)) {
std::cerr << "Trying to correct: " << bg::dsv(box) << " (" << reason << ")" << std::endl;
bg::correct(box);
assert(bg::is_valid(box));
}
for (auto it = qbegin(tree, RELATION(box)), e = qend(tree); it != e; ++it)
return false;
tree.insert(std::make_pair(box, index));
return true;
}
int main() {
std::cout << std::boolalpha;
RTree tree;
struct {
Box box;
char const* name;
char const* color;
} const shapes[]{
{{{1.00, 1.00}, {3.00, 3.00}}, "box1", "red"},
{{{4.00, 1.00}, {9.00, 5.00}}, "box2", "green"},
{{{1.00, 4.00}, {2.00, 9.00}}, "box3", "blue"},
{{{1.00, 2.75}, {2.00, 9.00}}, "probe1", "orange"},
{{{1.00, 4.00}, {3.50, 6.00}}, "probe2", "gray"},
};
for (auto const& s : shapes) {
auto idx = (&s - shapes);
auto added = add(tree, idx, s.box);
std::cout << "Adding " << s.name << " as #" << idx << ": "
<< (added ? "ACCEPT" : "REJECTED") << " " << bg::dsv(s.box)
<< "\n";
if (!added) {
for (auto it = qbegin(tree, RELATION(s.box)), e = qend(tree); it != e; ++it) {
std::cout << " - because " << s.name << " " << RELATION_STR
<< " " << shapes[it->second].name << "\n";
}
}
}
{
std::ofstream ofs("output.svg");
bg::svg_mapper<Point> svg(ofs, 400, 400);
auto style = [](std::string c) {
return "fill-rule:nonzero;fill-opacity:0.25;fill:" + c +
";stroke:" + c + ";stroke-width:1;";
};
for (auto const& [b, name, color] : shapes) {
svg.add(b);
}
for (auto const& [b, name, color] : shapes) {
auto h = b.max_corner().get<1>() - b.min_corner().get<1>();
auto mc = b.max_corner();
mc.set<1>(mc.get<1>() - h / 2);
svg.text(mc,
name +
("\n" + boost::lexical_cast<std::string>(bg::dsv(b))),
style(color));
}
for (auto const& [b, _, color] : shapes)
svg.map(b, style(color));
}
}
With either -DRELATION=bgi::intersects or -DRELATION=!bgi::disjoint:
Adding box1 as #0: ACCEPT ((1, 1), (3, 3))
Adding box2 as #1: ACCEPT ((4, 1), (9, 5))
Adding box3 as #2: ACCEPT ((1, 4), (2, 9))
Adding probe1 as #3: REJECTED ((1, 2.75), (2, 9))
- because probe1 bgi::intersects box1
- because probe1 bgi::intersects box3
Adding probe2 as #4: REJECTED ((1, 4), (3.5, 6))
- because probe2 bgi::intersects box3
(replacing bgi::intersects with !bgi::disjoint in the output obviously).
Visualization:
Note how the output changes if you change probe1:
{{{1.00, 2.75}, {1.50, 9.00}}, "probe1", "orange"},
That looks like:
This would behave as you expected even with the bgi::overlaps. Overlaps is just too strict for the expectated output.
I have a class named ItemType. It has two members - both double, named m_t and m_f. Two items of type ItemType are considered to be equal if these two members differ from each other within respective tolerance levels. With this logic, the comparator function is so defined as well. However, when I insert objects of this type as key into a map, only one key is produced in the map, even though at least three such keys should be present:
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
class ItemKey
{
public:
ItemKey(double t, double f)
{
m_t = t;
m_f = f;
}
double m_t;
double m_f;
double m_tEpsilon = 3;
double m_fEpsilon = 0.1;
bool operator<(const ItemKey& itemKey) const
{
int s_cmp = (abs(itemKey.m_f - m_f) > m_fEpsilon);
if (s_cmp == 0)
{
return (abs(itemKey.m_t - m_t) > m_tEpsilon);
}
return s_cmp < 0;
}
};
int main()
{
// The pairs are the respective values of m_t and m_f.
vector<pair<double, double>> pairs;
// These two should belong in one bucket -> (109.9, 9.0), because m_f differs by 0.09 and m_t differs by just 1
pairs.emplace_back(109.9, 9.0);
pairs.emplace_back(110.9, 9.09);
// This one is separate from above two beause even though m_t is in range, m_f is beyong tolerance level
pairs.emplace_back(109.5, 10.0);
// Same for this as well, here both m_t and m_f are beyong tolerance of any of the two categories found above
pairs.emplace_back(119.9, 19.0);
// This one matches the second bucket - (109.5, 10.0)
pairs.emplace_back(109.9, 10.05);
// And this one too.
pairs.emplace_back(111.9, 9.87);
map<ItemKey, size_t> itemMap;
for (const auto& item: pairs)
{
ItemKey key(item.first, item.second);
auto iter = itemMap.find(key);
if (iter == itemMap.end())
{
itemMap[key] = 1;
}
else
{
itemMap[iter->first] = itemMap[iter->first] + 1;
}
}
// The map should have three keys - (109.9, 9.0) -> count 2, (109.5, 10.0) -> count 3 and (119.9, 19.0) -> count 1
cout << itemMap.size();
}
However, the map seems to have only 1 key. How do I make it work as expected?
Why isn't your version working?
You did well to create your own comparison function. To answer your question, you have an error in your operator<() function such that only returns true if m_f is outside of tolerance and m_t is within tolerance, which I'm guessing is not what you desired. Let's take a look.
int s_cmp = (abs(itemKey.m_f - m_f) > m_fEpsilon);
The above line basically is checking whether this->m_f and itemKey.m_f are within tolerance of eachother (meaning equal to each other). That is probably what was intended. Then you say
if (s_cmp == 0)
{
return (abs(itemKey.m_t - m_t) > m_tEpsilon);
}
If s_cmp is true, then it will have the value of 1, and it will have a value of 0 for false (meaning that they are not within tolerance of each other). Then you return true if the m_t value is within tolerance. Up to this point, you return true if m_f is not equal (according to tolerance) and if m_t is equal (according to tolerance). Then your last line of code
return s_cmp < 0;
will return true always since a boolean converted to an integer cannot ever be negative.
How to get it working?
#include <iostream>
#include <string>
#include <map>
#include <cmath>
#include <vector>
struct ItemKey
{
double m_t;
double m_f;
static constexpr double t_eps = 3;
static constexpr double f_eps = 0.1;
ItemKey(double t, double f) : m_t(t), m_f(f) {}
bool operator<(const ItemKey& other) const
{
// Here it is assumed that f_eps and t_eps are positive
// We also ignore overflow, underflow, and NaN
// This is written for readability, and assumed the compiler will be
// able to optimize it.
auto fuzzy_less_than = [] (double a, double b, double eps) {
return a < b - eps;
};
bool f_is_less_than = fuzzy_less_than(this->m_f, other.m_f, f_eps);
bool f_is_greater_than = fuzzy_less_than(other.m_f, this->m_f, f_eps);
bool f_is_equal = !f_is_less_than && !f_is_greater_than;
bool t_is_less_than = fuzzy_less_than(this->m_t, other.m_t, t_eps);
return f_is_less_than || (f_is_equal && t_is_less_than);
}
};
int main()
{
using namespace std;
// The pairs are the respective values of m_t and m_f.
vector<pair<double, double>> pairs;
// These two should belong in one bucket
// -> (109.9, 9.0), because m_f differs by 0.09 and m_t differs by just 1
pairs.emplace_back(109.9, 9.0);
pairs.emplace_back(110.9, 9.09);
// This one is separate from above two beause even though m_t is in range,
// m_f is beyong tolerance level
pairs.emplace_back(109.5, 10.0);
// Same for this as well, here both m_t and m_f are beyong tolerance of any
// of the two categories found above
pairs.emplace_back(119.9, 19.0);
// This one matches the second bucket - (109.5, 10.0)
pairs.emplace_back(109.9, 10.05);
// And this one too.
pairs.emplace_back(111.9, 9.87);
map<ItemKey, size_t> itemMap;
for (const auto& item: pairs)
{
ItemKey key(item.first, item.second);
auto iter = itemMap.find(key);
if (iter == itemMap.end())
{
itemMap[key] = 1;
}
else
{
itemMap[iter->first] = itemMap[iter->first] + 1;
}
}
// The map should have three keys
// - (109.9, 9.0) -> count 2
// - (109.5, 10.0) -> count 3
// - (119.9, 19.0) -> count 1
cout << itemMap.size();
cout << "itemMap contents:" << endl;
for (auto& item : itemMap) {
cout << " (" << item.first << ", " << ")" << endl;
}
return 0;
}
There are a few things I changed above. I have a few suggestions also unrelated to the programming mistake:
Do not store boolean values into integer variables.
There's a reason that C++ introduced the bool type.
Write your code to be readable and in a way that the compiler
can easily optimize. You may notice I used a lambda expression
and multiple booleans. Smart compilers will inline the calls to
that lambda expression since it is only used within the local scope.
Also smart compilers can simplify boolean logic and make it
performant for me.
The m_tEpsilon and m_fEpsilon are probably not good to be
changable variables of the class. In fact, it may be bad if one
object has a different epsilon than another one. If that were the
case, which do you use when you do the < operator? For this
reason, I set them as static const variables in the class.
For constructors, it is better to initialize your variables in the
initializer list rather than in the body of the constructor. That
is unless you are doing dynamic resource allocation, then you would
want to do it in the constructor and make sure to clean it up if
you end up throwing an exception (preferrably using the RAII
pattern). I'm starting to get too far off topic :)
Even though class and struct are basically identical except for
the default protection level (class is private by default and
struct is public by default). It is convention to have it as a
struct if you want direct access to the member variables. Although,
in this case, I would probably set your class as immutable. To do
that, set the m_t and m_f as private variables and have a getter
m() and f(). It might be a bad idea to modify an ItemKey
instance in a map after it has been inserted.
Potential problems with this approach
One of the problems you have with your approach here is that it will be dependent on the order in which you add elements. Consider the following pairs to be added: (3.0, 10.0) (5.0, 10.0) (7.0, 10.0). If we add them in that order, we will get (3.0, 10.0) (7.0, 10.0), since (5.0, 10.0) was deemed to be equal to (3.0, 10.0). But what if we were to have inserted (5.0, 10.0) first, then the other two? Well then the list would only have one element, (5.0, 10.0), since bother of the others would be considered equal to this one.
Instead, I would like to suggest that you use std::multiset instead, of course this will depend on your application. Consider these tests:
void simple_test_map() {
std::map<ItemKey, size_t> counter1;
counter1[{3.0, 10.0}] += 1;
counter1[{5.0, 10.0}] += 1;
counter1[{7.0, 10.0}] += 1;
for (auto &itempair : counter1) {
std::cout << "simple_test_map()::counter1: ("
<< itempair.first.m_t << ", "
<< itempair.first.m_f << ") - "
<< itempair.second << "\n";
}
std::cout << std::endl;
std::map<ItemKey, size_t> counter2;
counter2[{5.0, 10.0}] += 1;
counter2[{3.0, 10.0}] += 1;
counter2[{7.0, 10.0}] += 1;
for (auto &itempair : counter2) {
std::cout << "simple_test_map()::counter2: ("
<< itempair.first.m_t << ", "
<< itempair.first.m_f << ") - "
<< itempair.second << "\n";
}
std::cout << std::endl;
}
This outputs:
simple_test_map()::counter1: (3, 10) - 2
simple_test_map()::counter1: (7, 10) - 1
simple_test_map()::counter2: (5, 10) - 3
And for the multiset variant:
void simple_test_multiset() {
std::multiset<ItemKey> counter1 {{3.0, 10.0}, {5.0, 10.0}, {7.0, 10.0}};
for (auto &item : counter1) {
std::cout << "simple_test_multiset()::counter1: ("
<< item.m_t << ", "
<< item.m_f << ")\n";
}
std::cout << std::endl;
std::multiset<ItemKey> counter2 {{5.0, 10.0}, {3.0, 10.0}, {7.0, 10.0}};
for (auto &item : counter2) {
std::cout << "simple_test_multiset()::counter2: ("
<< item.m_t << ", "
<< item.m_f << ")\n";
}
std::cout << std::endl;
std::cout << "simple_test_multiset()::counter2.size() = "
<< counter2.size() << std::endl;
for (auto &item : counter1) {
std::cout << "simple_test_multiset()::counter2.count({"
<< item.m_t << ", "
<< item.m_f << "}) = "
<< counter1.count(item) << std::endl;
}
std::cout << std::endl;
}
This outputs
simple_test_multiset()::counter1: (3, 10)
simple_test_multiset()::counter1: (5, 10)
simple_test_multiset()::counter1: (7, 10)
simple_test_multiset()::counter2: (5, 10)
simple_test_multiset()::counter2: (3, 10)
simple_test_multiset()::counter2: (7, 10)
simple_test_multiset()::counter2.count({3, 10}) = 2
simple_test_multiset()::counter2.count({5, 10}) = 3
simple_test_multiset()::counter2.count({7, 10}) = 2
simple_test_multiset()::counter2.size() = 3
Note that count() here returns the number of elements within the multiset that are considered equal to the ItemKey passed in. This may be advantageous for situations where you want to ask "how many of my points are within my tolerance of a new point?"
Good luck!
This question already has answers here:
How can I print a list of elements separated by commas?
(34 answers)
Closed 7 years ago.
For example:
for( auto &iter: item_vector ) {
if(not_on_the_last_element) printf(", ");
}
or
for( auto &iter: skill_level_map ) {
if(not_on_the_last_element) printf(", ");
}
You can't really. That's kind of the point of range-for, is that you don't need iterators. But you can just change your logic on how you print your comma to print it if it's not first:
bool first = true;
for (auto& elem : item_vector) {
if (!first) printf(", ");
// print elem
first = false;
}
If that's the intent of the loop anyway. Or you could compare the addresses:
for (auto& elem : item_vector) {
if (&elem != &item_vector.back()) printf(", ");
// ...
}
There's no great method. But if we have easy access to the last element of the container...
std::vector<int> item_vector = ...;
for (auto & elem : item_vector) {
...
if (&elem != &item_vector.back())
printf(", ");
}
These type of loops are best written using the "Loop and a Half" construct:
#include <iostream>
#include <vector>
int main()
{
auto somelist = std::vector<int>{1,2,3,4,5,6,6,7,8,9,6};
auto first = begin(somelist), last = end(somelist);
if (first != last) { // initial check
while (true) {
std::cout << *first++;
if (first == last) break; // check in the middle
std::cout << ", ";
}
}
}
Live Example that prints
1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 6
i.e. without a separator at the end of the last element.
The check in the middle is what makes this different from do-while (check up front) or for_each / range-based for (check at the end). Trying to force a regular for loop on these loops will introduce either extra conditional branches or duplicate program logic.
This is like a State Pattern.
#include <iostream>
#include <vector>
#include <functional>
int main() {
std::vector<int> example = {1,2,3,4,5};
typedef std::function<void(void)> Call;
Call f = [](){};
Call printComma = [](){ std::cout << ", "; };
Call noPrint = [&](){ f=printComma; };
f = noPrint;
for(const auto& e:example){
f();
std::cout << e;
}
return 0;
}
Output:
1, 2, 3, 4, 5
The first time through f points to noPrint which only serves to make f then point to printComma, so commas are only printed before the second and subsequent items.
store this code away safely in a header file in your little bag of utilities:
namespace detail {
template<class Iter>
struct sequence_emitter
{
sequence_emitter(Iter first, Iter last, std::string sep)
: _first(std::move(first))
, _last(std::move(last))
, _sep(std::move(sep))
{}
void write(std::ostream& os) const {
bool first_element = true;
for (auto current = _first ; current != _last ; ++current, first_element = false)
{
if (!first_element)
os << _sep;
os << *current;
}
}
private:
Iter _first, _last;
std::string _sep;
};
template<class Iter>
std::ostream& operator<<(std::ostream& os, const sequence_emitter<Iter>& se) {
se.write(os);
return os;
}
}
template<class Iter>
detail::sequence_emitter<Iter>
emit_sequence(Iter first, Iter last, std::string separator = ", ")
{
return detail::sequence_emitter<Iter>(std::move(first), std::move(last), std::move(separator));
}
then you can emit any range of any container without a trailing separator like this:
vector<int> x { 0, 1, 2, 3, 4, 5 };
cout << emit_sequence(begin(x), end(x)) << endl;
set<string> s { "foo", "bar", "baz" };
cout << emit_sequence(begin(s), end(s), " comes before ") << endl;
expected output:
0, 1, 2, 3, 4, 5
bar comes before baz comes before foo
Range based for loop are made to iterate over the whole range. If you do not want that, why don't you just do a regular for loop?
auto end = vector.end() - 1;
for (auto iter = vector.begin(); iter != end; ++iter) {
// do your thing
printf(", ");
}
// do your thing for the last element
If you do not want to repeat the code twice to "do your thing", as I would, then create a lambda that does it a call it:
auto end = vector.end() - 1;
// create lambda
for (auto iter = vector.begin(); iter != end; ++iter) {
lambda(*iter);
printf(", ");
}
lambda(vector.back());
I would like to know if the attached code correctly implements a custom C++ input iterator.
While I am able to compile it successfully and obtain the expected results, I am doubtful that I have fully covered all the input iterator concept requirements.
My concrete question: is this how you implement a custom C++ input iterator?
I have studied other answers to similar questions, and have read the relevant section in Josuttis book. Unfortunately those resources do not offer more extensive information, and even though I am able to code something that "seems to work", I do not trust my own code.
The example implements an input iterator which yields n values in [initial, final]; the iterator is named after the well-known Matlab function offering the same functionality:
#include <cstddef> // for std::size_t
#include <iterator> // for std::iterator
#include <iostream> // for std::cout
#include <algorithm> // for std::copy
#include <vector> // for std::vector
template<typename T = double>
class linspace_iterator
: public std::iterator<std::input_iterator_tag,T> {
public:
/// #brief Constructs the end-of-linspace iterator.
explicit linspace_iterator()
: m_initial(0),
m_final(0),
m_step(0),
m_N(0),
m_k(0) { }
/// #brief Constructs an iterator yielding N equally-spaced values
/// in #p [initial, final].
explicit linspace_iterator(T initial, T final, std::size_t N = 100)
: m_initial(std::move(initial)),
m_final(std::move(final)),
m_step((final - initial) / (N - 1)),
m_N(std::move(N)),
m_k(0) { }
T operator*() const {
return m_initial + m_k * m_step;
}
linspace_iterator<T>& operator++() {
m_k++;
return *this;
}
linspace_iterator<T>& operator++(int n) {
m_k += n;
return *this;
}
bool operator !=(linspace_iterator<T>&) const { return m_k != m_N; }
bool operator ==(linspace_iterator<T>&) const { return m_k == m_N; }
private:
const T m_initial;
const T m_final;
const T m_step;
const std::size_t m_N;
std::size_t m_k;
};
template<typename Container>
Container linspace(typename Container::value_type initial,
typename Container::value_type final,
std::size_t N = 100) {
return Container(linspace_iterator<typename Container::value_type>(initial, final, N),
linspace_iterator<typename Container::value_type>());
}
template<typename Container>
inline void show(const Container& c) {
for(const auto & item : c) {
std::cout << item << " ";
}
std::cout << std::endl;
}
int main() {
std::cout << "Copy linspace to std::cout (5 values in [0, 7])" << std::endl;
std::copy(linspace_iterator<>(0, 7, 5),
linspace_iterator<>(),
std::ostream_iterator<double>(std::cout, ", "));
std::cout << "A vector with 100 values in [-1, 1]" << std::endl;
std::vector<double> data(linspace_iterator<>(-1, 1),
linspace_iterator<>());
show(data);
std::cout << "A vector with 100 values in [-5.0f, 2.0f]" << std::endl;
auto myvec = linspace<std::vector<float>>(-5.0f, 2.0f);
show(myvec);
std::cout << "Addressing #mythagel's comment" << std::endl;
for(auto it = linspace_iterator<>(0, 7, 5), end = linspace_iterator<>(); it != end; ++it) {
std::cout << *it << ' ' << *it << '\n';
}
return 0;
}
Using the following command:
{cxx} linspace-itertor.cpp -std=c++11 -Wall -Wextra -Werror -pedantic
(where {cxx} = g++ 4.8.2, icpc 14.0.2, and clang++ 3.6.0) the code compiles successfully (no warnings, no errors).
Output:
Copy linspace to std::cout (5 values in [0, 7])
0, 1.75, 3.5, 5.25, 7, A vector with 100 values in [-1, 1]
-1 -0.979798 -0.959596 -0.939394 -0.919192 -0.89899 -0.878788 -0.858586 -0.838384 -0.818182 -0.79798 -0.777778 -0.757576 -0.737374 -0.717172 -0.69697 -0.676768 -0.656566 -0.636364 -0.616162 -0.59596 -0.575758 -0.555556 -0.535354 -0.515152 -0.494949 -0.474747 -0.454545 -0.434343 -0.414141 -0.393939 -0.373737 -0.353535 -0.333333 -0.313131 -0.292929 -0.272727 -0.252525 -0.232323 -0.212121 -0.191919 -0.171717 -0.151515 -0.131313 -0.111111 -0.0909091 -0.0707071 -0.0505051 -0.030303 -0.010101 0.010101 0.030303 0.0505051 0.0707071 0.0909091 0.111111 0.131313 0.151515 0.171717 0.191919 0.212121 0.232323 0.252525 0.272727 0.292929 0.313131 0.333333 0.353535 0.373737 0.393939 0.414141 0.434343 0.454545 0.474747 0.494949 0.515152 0.535354 0.555556 0.575758 0.59596 0.616162 0.636364 0.656566 0.676768 0.69697 0.717172 0.737374 0.757576 0.777778 0.79798 0.818182 0.838384 0.858586 0.878788 0.89899 0.919192 0.939394 0.959596 0.979798 1
A vector with 100 values in [-5.0f, 2.0f]
-5 -4.92929 -4.85859 -4.78788 -4.71717 -4.64646 -4.57576 -4.50505 -4.43434 -4.36364 -4.29293 -4.22222 -4.15152 -4.08081 -4.0101 -3.93939 -3.86869 -3.79798 -3.72727 -3.65657 -3.58586 -3.51515 -3.44444 -3.37374 -3.30303 -3.23232 -3.16162 -3.09091 -3.0202 -2.9495 -2.87879 -2.80808 -2.73737 -2.66667 -2.59596 -2.52525 -2.45455 -2.38384 -2.31313 -2.24242 -2.17172 -2.10101 -2.0303 -1.9596 -1.88889 -1.81818 -1.74747 -1.67677 -1.60606 -1.53535 -1.46465 -1.39394 -1.32323 -1.25253 -1.18182 -1.11111 -1.0404 -0.969697 -0.89899 -0.828283 -0.757576 -0.686869 -0.616162 -0.545455 -0.474748 -0.404041 -0.333333 -0.262627 -0.191919 -0.121212 -0.0505052 0.0202017 0.090909 0.161616 0.232323 0.30303 0.373737 0.444444 0.515152 0.585858 0.656566 0.727273 0.797979 0.868687 0.939394 1.0101 1.08081 1.15152 1.22222 1.29293 1.36364 1.43434 1.50505 1.57576 1.64646 1.71717 1.78788 1.85859 1.92929 2
Addressing #mythagel's comment
0 0
1.75 1.75
3.5 3.5
5.25 5.25
7 7