Questions on some code using boost::zip_iterator - c++

Recently I saw some example code on how to use boost::zip_iterator. However, I can't figure out how it works. Here is the code:
class to_hex2
{
private:
vector<unsigned char> &v;
char trans(const char c) const
{
if(c >= 'a')
return c - 'a' + 10;
else if(c >= 'A')
return c - 'A' + 10;
else
return c - '0';
}
public:
to_hex2(vector<unsigned char> &_v):
v(_v){}
typedef boost::tuples::tuple<const char&,const char&> Tuple;
void operator()(Tuple const &t) const
{
static char tmp;
tmp = trans(t.get<0>()) * 0x10;
tmp += trans(t.get<1>());
v.push_back(tmp);
}
};
int main()
{
char s[] = "1234aBcD";
vector<unsigned char> v;
typedef step_iterator<const char*> si_t;
for_each(
boost::make_zip_iterator(
boost::tuples::make_tuple(si_t(s),si_t(s+1))),
boost::make_zip_iterator(
boost::tuples::make_tuple(si_t(s+8),si_t(s+9))),
to_hex2(v));
std::copy(
v.begin(),v.end(),std::ostream_iterator<unsigned char>(cout," "));
std::cout<<std::endl<<"v.size="<<v.size();
return 0;
}
step_iterator is an iterator that iterates two steps instead of one.
My first question is: Is it OK to write s+9 since the index of array s is up to 8(including '\0':-) )? The code seems to run properly although.
My second question is: Since zip_iterator makes it possible to iterate over a vector concurrently, does that mean the result is random? The result I see is constant, in the following picture:
Last but not least, could someone please tell me how is the result generated( what's the meaning of it) since there is no up-down arrow in ASCII codes( I googled it and saw it here).

It is ok to point one-past-the-end of an array, as long as you don't dereference the pointer. This is very useful because C++ uses half-open ranges, where the last element is excluded.
In the code you posted, s+9 points one-past-the-end of s, but is never dereferenced, so the behavior is well-defined.
Regarding your second question: no, the result of this code is not random. The elements will be iterated over in order, from first to last. When the documentation states that zip_iterator allows parallel iteration over a sequence, it does not mean that the iteration will be performed concurrently by several threads or whatever, it only means that each iteration will advance several iterators instead of only one. Here is a possible implementation of for_each:
template <typename InputIterator, typename Func>
void for_each(InputIterator first, InputIterator last, Func f)
{
while (first != last)
{
f(*first);
++first;
}
}
As you see, for_each works on a single iterator. If you need to iterate over two sequences at a time, then you can use zip_iterator, which encapsulates several iterators. Its operator* returns multiple values (a tuple), and its operator++s increments all the iterators, advancing them simultaneously.
To better understand what is going on in your code, here is a streamlined version, without zip_iterator and for_each:
class to_hex2
{
private:
vector<unsigned char> &v;
char trans(const char c) const
{
if(c >= 'a')
return c - 'a' + 10;
else if(c >= 'A')
return c - 'A' + 10;
else
return c - '0';
}
public:
to_hex2(vector<unsigned char> &_v):
v(_v){}
void operator()(const char &first, const char &second) const
{
static char tmp;
tmp = trans(first) * 0x10;
tmp += trans(second);
v.push_back(tmp);
}
};
int main()
{
char s[] = "1234aBcD";
vector<unsigned char> v;
to_hex2 transformer(v);
char *first = s;
char *second = s + 1;
for ( ; first != s + 8 && second != s + 9 ; first += 2, second += 2)
{
transformer(*first, *second);
}
std::copy(v.begin(),v.end(),
std::ostream_iterator<unsigned char>(cout," "));
std::cout<<std::endl<<"v.size="<<v.size();
return 0;
}
Hopefully, this should make it clear that zip_iterator is just a convenient way of making several iterators advance at the same time.
Finally, to understand the purpose of this code, you should probably print the result as integers rather than as characters. You should see this:
18 52 171 205
which are the decimal representation of the hexadecimal numbers contained in the original string (1216 = 1810, 3416 = 5210, AB16 = 17110 and CD16 = 20510). So basically, v contains the representation in base 256 of the original hexadecimal string.

Related

Converting a for loop to use std::transform instead

I'm working on a function and it currently works as is.
void ceasarShift( CeasarText& text, int amount ) {
// Bound amount to the number of characters in the alphabet
amount %= 26;
for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}
}
The class that this function takes is a simple class that has 2 std::string member variables. When this class is passed by reference where this is a friend function to that class, it takes the original string that is already set and uses that to populate the shifted string which is empty before this function sees it. When the for loop is done, the function is finished and the class object is returned back by reference with a newly updated shifted string and the original is unmodified.
I was thinking of maybe using std::transform and a lambda to do the same task. This is what I have tried.
void ceasarShift( CeasarText& text, int amount ) {
// Bound amount to the number of characters in the alphabet
amount %= 26;
/*for ( std::size_t i = 0; i < text._originalText.length(); i++ ) {
char c = text._originalText[i] + amount;
text._shiftedText += c;
}*/
std::transform( text._originalText.begin(), text._originalText.end(),
text._shiftedText.begin(),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);
}
And I end up getting a Debug Assertion Failed! Window message Expression: cannot seek string iterator past end
I'm wondering if my lambda is wrong or if I need to use something else other than std::transform.
Edit
I have also tried this and this works like the first loop:
{
amount %= 26;
for ( auto& c : text._originalText )
text._shiftedText += (c + amount);
}
I just can not seem to get the lambda to work properly as the predicate to std::transform.
The problem is not your lambda. The difference between your two snippets, is that your for loop appends elements to your string, using operator+=, but your transform assumes that the elements already exist. If you want to append to the string with transform, then you will need to use an insert iterator. Specifically a back insert iterator. So instead of passing text._shiftedText.begin(), pass std::back_inserter(text._shiftedText).
std::transform( text._originalText.begin(), text._originalText.end(),
std::back_inserter(text._shiftedText),
[amount]( unsigned char c ) -> unsigned char { return c + amount; }
);
According to this link, The behavior of std::transform is equivalent to:
template <class InputIterator, class OutputIterator, class UnaryOperator>
OutputIterator transform (InputIterator first1, InputIterator last1,
OutputIterator result, UnaryOperator op)
{
while (first1 != last1) {
*result = op(*first1); // or: *result=binary_op(*first1,*first2++);
++result; ++first1;
}
return result;
}
which means the container of OutputIterator should reserve enough space, otherwise, it will out of range when ++result

Forming the maximum number out of the available numbers?

I am trying to solve the following question https://www.interviewbit.com/problems/largest-number/ : Given a list of non negative integers, arrange them such that they form the largest number.
For example:
Given [3, 30, 34, 5, 9], the largest formed number is 9534330.
Note: The result may be very large, so you need to return a string instead of an integer.
I have been able to solve it and implemented it, using comparison based sorting technique. That is, given two numbers X and Y, I compare two numbers XY (Y appended at the end of X) and YX (X appended at the end of Y). If XY is larger, then X should come before Y in output, else Y should come before. The following is the code:
string Solution::largestNumber(const vector<int> &A) {
// Do not write main() function.
// Do not read input, instead use the arguments to the function.
// Do not print the output, instead return values as specified
// Still have a doubt. Checkout www.interviewbit.com/pages/sample_codes/ for more details
vector<string> myvec;
for (int i = 0; i < A.size(); i++)
{
string s = to_string(A[i]);
myvec.push_back(s);
}
sort(myvec.begin(),myvec.end(),mycomp());
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
}
struct mycomp
{
inline bool operator() (const string &p1, const string &p2)
{
string s1 = p1.append(p2);
string s2 = p2.append(p1);
if (s1.compare(s2) < 0)
return false;
else
return true;
}
};
But, the problem is, I have to merge the two functions into a single one because I just have to implement the single function. I cannot define one more function since I have no control over the entire piece of code (look at the link's submission part). Therefore, my ask is, how can I use the comparator by defining it inside the function string Solution::largestNumber(const vector<int> &A). Thanks!
This is a perfect place for a lambda.
sort(myvec.begin(), myvec.end(), [](const string &p1, const string &p2) {
string s1(p1 + p2);
string s2(p2 + p1);
return s1.compare(s2) >= 0;
});
I changed your code to not call append() on the strings, since you accept them as references to const objects, and p1.append(p2) tries to modify p1, but that's not allowed on a const object. Further, avoid constructs like if(x) return true else return false; and instead just return x;
Also, this
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
Can be condensed to:
string s;
for (auto const& e : myvec)
s += e;
return s;
(Assuming you have a c++11 compiler or later)

C++: Equality of struct of strings

In my code I have a struct, similar to the following:
struct basket {
std::string a;
std::string b;
std::string c;
std::string d;
};
I want to be able to compare two structs and determine > 0, < 0, == 0 by comparing the concatenation of all of those strings
std::string total = (a+b+c+d);
However, I want to achieve this without doing the actual concatenation, because this comparison is used many times, and it ends up being a run-time bottleneck. I know that if that's the case, I should look into avoiding the use of strings, but for now I'd simply like to do this comparison easily without concatenating.
Right now, I use a giant if statement. For example compare string a from each instance of the struct, if they're the same, compare b, if they're the same, compare c, if they're the same then finally compare d, but I was wondering if there's a cleaner way to do this in c++ that doesn't have the runtime hit of concatenations.
Thank you.
If I understand you correctly, you want two structs to be equal if the concatenation of strings is equal, so
a == "hello", b == "there", ...
matches
a = "hel", b == "lothere", ...
I would do this using boost::range::join:
struct basket {
...
bool operator==(const basket& other) const
{
using namespace boost::range;
auto left = join(join(join(a, b), c), d);
auto right = join(join(join(other.a, other.b), other.c), other.d);
return equal(left, right); // http://www.boost.org/doc/libs/1_51_0/libs/range/doc/html/range/reference/algorithms/non_mutating/equal.html
}
};
boost::range::join (http://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/reference/utilities/join.html) creates a joined range without doing the concatenation. It internally just iterates all the way across. The only additional overhead is checking for the end of the first range and moving to the second range, so it should be much faster than actually concatenating.
UPDATE:
I originally missed the desire for a strcmp style return. Unfortunately I can't find any standard algorithms that return a value like this. On the upside, though, it's easy enough to write. Here is the update version, with compare instead of equal:
#include <boost/range/join.hpp>
template <typename SinglePassRange1, typename SinglePassRange2>
int compare(const SinglePassRange1& left, const SinglePassRange2& right)
{
using namespace std;
auto leftIt = begin(left);
auto leftEnd = end(right);
auto rightIt = begin(right);
auto rightEnd = end(right);
for ( ; leftIt != leftEnd
&& rightIt != rightEnd
&& *leftIt == *rightIt
; ++leftIt, ++rightIt)
{
}
// should be safe since one-past-end for strings is '\0'
return static_cast<int>(*leftIt) - static_cast<int>(*rightIt);
}
struct basket {
std::string a;
std::string b;
std::string c;
std::string d;
int compare(const basket& other) const
{
using namespace boost::range;
auto left = join(join(join(a, b), c), d);
auto right = join(join(join(other.a, other.b), other.c), other.d);
return ::compare(left, right);
}
};
Tested on GCC 4.9.1 Ubuntu.
That compare free function should probably be called compareStringRanges or something since that implementation is only valid for strings. I'll leave that to the realm of individual aesthetics.
The easy way to work with these fields would be to use an array for fields' storage, rather than individual fields.
You can create a temporary array to simplify your code. Your implementation will almost surely put the std::array's data on the stack.
Illustration:
int compare(const basket &pOther) const {
const BasketStringRefs a(this->allStrings());
const BasketStringRefs b(pOther.allStrings());
// ...your evaluation here, involving iteration over a and b...
}
private:
typedef std::array<const std::string*, 4> BasketStringRefs;
BasketStringRefs allStrings() const {
return BasketStringRefs{{&a, &b, &c, &d}};
}
It'll be trouble to set up, but it'll be efficient: store a, b, c, and d in a single buffer. You can maintain pointers to each to keep track of where each begins (or if you know a max size, store them at relative locations 0, max size, max size*2, and max size+3).
When you compare baskets, compare their buffers, using a for loop that goes up to buffer size rather than a while that ends with the null character. When you get to a null character, you're done with that string; go on to the next one to see the next char you want to compare. If that happens 4 times, you've read all 4, and should quit that basket.
//baskets are a and b
for (int aIndex = 0, bindex = 0; aIndex < MAX && bIndex < MAX; ++aIndex, ++bIndex)
{
//if we reach null char for basket a, skip to next string in basket a. Same for b.
//if we run out of strings in basket a first, it's shorter
// and comes first in alpha order. Same for b.
//if we run out of strings in both baskets without finding any differences,
// return 0 for equal
//compare the next char in each buffer; if different, return -1 or 1 appropriately
}
I wrote a quick and dirty iterator for the concatenated string, just for fun:
#include "stdafx.h"
#include <iostream>
#include <assert.h>
class concatenation {
private:
std::string a, b, c, d;
public:
concatenation(){}
concatenation(const std::string &s1, const std::string &s2, const std::string &s3, const std::string &s4)
: a(s1), b(s2), c(s3), d(s4)
{}
class const_iterator {
private:
size_t stringnumber, stringposition;
const concatenation &target;
public:
const_iterator(const concatenation &r, size_t n, size_t p) : target(r), stringnumber(n), stringposition(p) {}
bool operator ==(const const_iterator &rhs){ return stringnumber == rhs.stringnumber && stringposition == rhs.stringposition; }
bool operator !=(const const_iterator &rhs){ return !(*this == rhs); }
char operator *(){
switch (stringnumber){
case 0: return target.a[stringposition];
case 1: return target.b[stringposition];
case 2: return target.c[stringposition];
case 3: return target.d[stringposition];
default:
return '\0';
}
}
const_iterator& operator ++(){
size_t current_length;
switch (stringnumber){
case 0: current_length = target.a.size(); break;
case 1: current_length = target.b.size(); break;
case 2: current_length = target.c.size(); break;
case 3: current_length = target.d.size(); break;
default:
assert(0);
current_length = 0;
}
if (stringposition < current_length-1){
++stringposition;
} else {
stringposition = 0;
++stringnumber;
}
return *this;
}
};
const_iterator begin() const {
return const_iterator(*this, 0, 0);
}
const_iterator end() const {
return const_iterator(*this, 4, 0);
}
};
int compare(const concatenation &left, const concatenation &right){
concatenation::const_iterator p1 (left.begin());
concatenation::const_iterator p2 (right.begin());
while (p1 != left.end() && p2 != right.end() && (*p1)==(*p2)){
++p1;
++p2;
}
if (*p1 != *p2){
if (p1 == left.end() && p2 != right.end())
return -1;
if (p1 != left.end() && p2 == right.end())
return 1;
if (*p1 < *p2)
return -1;
else
return 1;
}
return 0;
}
int main()
{
concatenation test("hel", "lo ", "wor", "ld.");
for (concatenation::const_iterator pos = test.begin(); pos != test.end(); ++pos)
std::cout << *pos;
std::cout << std::endl;
{
// same
concatenation first("hello", " ", "world", ".");
concatenation second("hel", "lo ", "wor", "ld.");
if (0 != compare(first, second))
assert(0);
}
{
// first character different
concatenation first("hello", " ", "world", ".");
concatenation second("jel", "lo ", "wor", "ld.");
if (-1 != compare(first, second))
assert(0);
if (1 != compare(second, first))
assert(0);
}
{
// middle character different
concatenation first("hello", " ", "world", ".");
concatenation second("hel", "p! ", "wor", "ld.");
if (-1 != compare(first, second))
assert(0);
if (1 != compare(second, first))
assert(0);
}
{
// length different
concatenation first("hello", "", "", "");
concatenation second("hel", "lo ", "wor", "ld.");
if (-1 != compare(first, second))
assert(0);
if (1 != compare(second, first))
assert(0);
}
return 0;
}

Erasing elements in std::vector by using indexes

I've a std::vector<int> and I need to remove all elements at given indexes (the vector usually has high dimensionality). I would like to know, which is the most efficient way to do such an operation having in mind that the order of the original vector should be preserved.
Although, I found related posts on this issue, some of them needed to remove one single element or multiple elements where the remove-erase idiom seemed to be a good solution.
In my case, however, I need to delete multiple elements and since I'm using indexes instead of direct values, the remove-erase idiom can't be applied, right?
My code is given below and I would like to know if it's possible to do better than that in terms of efficiency?
bool find_element(const vector<int> & vMyVect, int nElem){
return (std::find(vMyVect.begin(), vMyVect.end(), nElem)!=vMyVect.end()) ? true : false;
}
void remove_elements(){
srand ( time(NULL) );
int nSize = 20;
std::vector<int> vMyValues;
for(int i = 0; i < nSize; ++i){
vMyValues.push_back(i);
}
int nRandIdx;
std::vector<int> vMyIndexes;
for(int i = 0; i < 6; ++i){
nRandIdx = rand() % nSize;
vMyIndexes.push_back(nRandIdx);
}
std::vector<int> vMyResult;
for(int i=0; i < (int)vMyValues.size(); i++){
if(!find_element(vMyIndexes,i)){
vMyResult.push_back(vMyValues[i]);
}
}
}
I think it could be more efficient, if you just just sort your indices and then delete those elements from your vector from the highest to the lowest. Deleting the highest index on a list will not invalidate the lower indices you want to delete, because only the elements higher than the deleted ones change their index.
If it is really more efficient will depend on how fast the sorting is. One more pro about this solultion is, that you don't need a copy of your value vector, you can work directly on the original vector. code should look something like this:
... fill up the vectors ...
sort (vMyIndexes.begin(), vMyIndexes.end());
for(int i=vMyIndexes.size() - 1; i >= 0; i--){
vMyValues.erase(vMyValues.begin() + vMyIndexes[i])
}
to avoid moving the same elements many times, we can move them by ranges between deleted indexes
// fill vMyIndexes, take care about duplicated values
vMyIndexes.push_back(-1); // to handle range from 0 to the first index to remove
vMyIndexes.push_back(vMyValues.size()); // to handle range from the last index to remove and to the end of values
std::sort(vMyIndexes.begin(), vMyIndexes.end());
std::vector<int>::iterator last = vMyValues.begin();
for (size_t i = 1; i != vMyIndexes.size(); ++i) {
size_t range_begin = vMyIndexes[i - 1] + 1;
size_t range_end = vMyIndexes[i];
std::copy(vMyValues.begin() + range_begin, vMyValues.begin() + range_end, last);
last += range_end - range_begin;
}
vMyValues.erase(last, vMyValues.end());
P.S. fixed a bug, thanks to Steve Jessop that patiently tried to show me it
Erase-remove multiple elements at given indices
Update: after the feedback on performance from #kory, I've modified the algorithm not to use flagging and move/copy elements in chunks (not one-by-one).
Notes:
indices need to be sorted and unique
uses std::move (replace with std::copy for c++98):
Github
Live example
Code:
template <class ForwardIt, class SortUniqIndsFwdIt>
inline ForwardIt remove_at(
ForwardIt first,
ForwardIt last,
SortUniqIndsFwdIt ii_first,
SortUniqIndsFwdIt ii_last)
{
if(ii_first == ii_last) // no indices-to-remove are given
return last;
typedef typename std::iterator_traits<ForwardIt>::difference_type diff_t;
typedef typename std::iterator_traits<SortUniqIndsFwdIt>::value_type ind_t;
ForwardIt destination = first + static_cast<diff_t>(*ii_first);
while(ii_first != ii_last)
{
// advance to an index after a chunk of elements-to-keep
for(ind_t cur = *ii_first++; ii_first != ii_last; ++ii_first)
{
const ind_t nxt = *ii_first;
if(nxt - cur > 1)
break;
cur = nxt;
}
// move the chunk of elements-to-keep to new destination
const ForwardIt source_first =
first + static_cast<diff_t>(*(ii_first - 1)) + 1;
const ForwardIt source_last =
ii_first != ii_last ? first + static_cast<diff_t>(*ii_first) : last;
std::move(source_first, source_last, destination);
// std::copy(source_first, source_last, destination) // c++98 version
destination += source_last - source_first;
}
return destination;
}
Usage example:
std::vector<int> v = /*...*/; // vector to remove elements from
std::vector<int> ii = /*...*/; // indices of elements to be removed
// prepare indices
std::sort(ii.begin(), ii.end());
ii.erase(std::unique(ii.begin(), ii.end()), ii.end());
// remove elements at indices
v.erase(remove_at(v.begin(), v.end(), ii.begin(), ii.end()), v.end());
What you can do is split the vector (actually any non-associative container) in two
groups, one corresponding to the indices to be erased and one containing the rest.
template<typename Cont, typename It>
auto ToggleIndices(Cont &cont, It beg, It end) -> decltype(std::end(cont))
{
int helpIndx(0);
return std::stable_partition(std::begin(cont), std::end(cont),
[&](typename Cont::value_type const& val) -> bool {
return std::find(beg, end, helpIndx++) != end;
});
}
you can then delete from (or up to) the split point to erase (keep only)
the elements corresponding to the indices
std::vector<int> v;
v.push_back(0);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
int ar[] = { 2, 0, 4 };
v.erase(ToggleIndices(v, std::begin(ar), std::end(ar)), v.end());
If the 'keep only by index' operation is not needed you can use remove_if insted of stable_partition (O(n) vs O(nlogn) complexity)
To work for C arrays as containers the lambda function should be
[&](decltype(*(std::begin(cont))) const& val) -> bool
{ return std::find(beg, end, helpIndx++) != end; }
but then the .erase() method is no longer an option
If you want to ensure that every element is only moved once, you can simply iterate through each element, copy those that are to remain into a new, second container, do not copy the ones you wish to remove, and then delete the old container and replace it with the new one :)
This is an algorithm based on Andriy Tylychko's answer so that this can make it easier and faster to use the answer, without having to pick it apart. It also removes the need to have -1 at the beginning of the indices list and a number of items at the end. Also some debugging code to make sure the indices are valid (sorted and valid index into items).
template <typename Items_it, typename Indices_it>
auto remove_indices(
Items_it items_begin, Items_it items_end
, Indices_it indices_begin, Indices_it indices_end
)
{
static_assert(
std::is_same_v<std::random_access_iterator_tag
, typename std::iterator_traits<Items_it>::iterator_category>
, "Can't remove items this way unless Items_it is a random access iterator");
size_t indices_size = std::distance(indices_begin, indices_end);
size_t items_size = std::distance(items_begin, items_end);
if (indices_size == 0) {
// Nothing to erase
return items_end;
}
// Debug check to see if the indices are already sorted and are less than
// size of items.
assert(indices_begin[0] < items_size);
assert(std::is_sorted(indices_begin, indices_end));
auto last = items_begin;
auto shift = [&last, &items_begin](size_t range_begin, size_t range_end) {
std::copy(items_begin + range_begin, items_begin + range_end, last);
last += range_end - range_begin;
};
size_t last_index = -1;
for (size_t i = 0; i != indices_size; ++i) {
shift(last_index + 1, indices_begin[i]);
last_index = indices_begin[i];
}
shift(last_index + 1, items_size);
return last;
}
Here is an example of usage:
template <typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T>& v)
{
for (auto i : v) {
os << i << " ";
}
os << std::endl;
return os;
}
int main()
{
using std::begin;
using std::end;
std::vector<int> items = { 1, 3, 6, 8, 13, 17 };
std::vector<int> indices = { 0, 1, 2, 3, 4 };
std::cout << items;
items.erase(
remove_indices(begin(items), end(items), begin(indices), end(indices))
, std::end(items)
);
std::cout << items;
return 0;
}
Output:
1 3 6 8 13 17
17
The headers required are:
#include <iterator>
#include <vector>
#include <iostream> // only needed for output
#include <cassert>
#include <type_traits>
And a Demo can be found on godbolt.org.

Comparing arrays of objects with arrays of fields of objects

Is there a good way to compare arr[i].A to A[i] and arr[i].B to B?
int A[10], B[10];
class Foo {
int A, B;
};
Foo arr[10];
I could do the following:
for (i=0;i<10;i++) {
if (A[i] == arr[i].A) {}
if (B[i] == arr[i].B) {}
}
But, this is painful especially if there are a lot of fields, and the if() conditional does the same thing over and over, there will be a lot of code duplication. What I really want to do is parametrize this somehow and call a function like (test(A,arr)). I guess I can solve this by using #define macros, but that seems ugly.
Any suggestions?
Also I want to avoid creating a new array of Foo objects because I don't want to create new objects that may have many fields I don't care about, also I may want to compare different subsets of fields.
IF the ranges are of equal size you can use std::equal with a predicate (or a lambda):
bool CompA( int lhs, Foo rhs ){
return lhs == rhs.A;
};
...
// to check for equality
bool result = std::equal( A, A + 10, arr, CompA );
...
// to see where the mismatch is
std::pair< int*, Foo* > result = std::mismatch( A, A + 10, arr, CompA );
size_t index = result.first - A;
if( index < 10 ){
std::cout << "Mismatch at index " << index << " A = " << *result.first << " Foo.A = " << (*result.second).A << std::endl;
}
There are standard-library algorithms for doing operations on containers (including arrays, kinda) but using them typically produces code that's harder to read and maintain, and no shorter or more efficient, than straightforward loops.
However, it sounds as if you might want to know about pointers-to-members.
bool all_equal(int Foo::* member, const Foo * obj_array, const int * elem_array, size_t n) {
for (int i=0; i<n; ++i) {
if (obj_array[i].*member != elem_array[i]) return false;
}
return true;
}
...
if (all_equal(&Foo::A, arr, A, 10) && all_equal(&Foo::*B, arr, B, 10)) ...
although actually you should probably generalize it:
template<typename T, typename E>
bool all_equal(E T::* member, const T* obj_array, const E* elem_array, size_t n) {
for (int i=0; i<n; ++i) {
if (obj_array[i].*member != elem_array[i]) return false;
}
return true;
}
(Danger: all code above is untested and may consist entirely of bugs.)