I have the following code:
#include "stdafx.h"
#include <map>
#include <string>
#include <iostream>
class MyObject
{
public:
MyObject()
: m_Items{ { 1, "one" },{ 2, "two" },{ 3, "three" } }
{}
RETURNTYPE GetStringIterator() const
{
IMPLEMENTATION
}
private:
std::map<int, std::string> m_Items;
};
int main()
{
MyObject o;
for (auto& s : o.GetStringIterator())
{
std::cout << s;
}
}
What should RETURNTYPE and IMPLEMENTATION be in order to allow any client of MyObject (in this case the main() function), to iterate over the values of the m_Items map, without copying any data? It seems that this should be possible with c++11 range based for loops and iterators. but I have not been able to figure out how.
range-based iteration can be achieved like this:
class MyObject
{
public:
MyObject()
: m_Items{ { 1, "one" },{ 2, "two" },{ 3, "three" } }
{}
auto begin() { return m_Items.begin(); }
auto begin() const { return m_Items.begin(); }
auto end() { return m_Items.end(); }
auto end() const { return m_Items.end(); }
private:
std::map<int, std::string> m_Items;
};
Copying or not copying the value depends on how the code is written at the call site:
MyObject a;
for(auto [key,value] : a) {} // copies are made
for(auto & [key,value] : a) {} // no copy
for(auto const & [key,value] : a) {} // no copy
And you can disable the modification of map values by removing the non-const versions of begin and end :
class MyObject
{
public:
MyObject()
: m_Items{ { 1, "one" },{ 2, "two" },{ 3, "three" } }
{}
auto begin() const { return m_Items.begin(); }
auto end() const { return m_Items.end(); }
private:
std::map<int, std::string> m_Items;
};
Then, attempts at modifying the value in a range-for loop will lead to a compilation error:
MyObject a;
for(auto & [key,value] : a) {
//value.push_back('a'); // Not OK
}
for(auto & [key,value] : a) {
cout << value; // OK
}
Note that if the map is an implementation detail, the answer proposed by #Barry should be used, because it iterates only on the values of the map, not on the keys too.
You could use boost::adaptors::map_values, it works in C++11:
auto GetStringIterator() const
// NB: have the move the declaration of m_Items ahead of this function for this to work
-> decltype(m_Items | boost::adaptors::map_values)
{
return m_Items | boost::adaptors::map_values;
}
Or its range-v3 equivalent, view::values. Both can be used like values(m) instead of m | values, if you prefer it that way.
Either solution returns a view onto the values of the map. This is an object that doesn't own any of its underlying elements and is cheap to copy - that is, O(1). We're not coyping the map, or any of its underlying elements.
You would use this as if it were any other range:
for (std::string const& s : o.GetStringIterator()) {
// ...
}
This loop does not copy any strings. Each s refers directly into the corresponding string that the map is storing.
I'm going to first answer this in c++14.
Here is a minimal mapping iteratoroid:
template<class F, class It>
struct iterator_mapped {
decltype(auto) operator*() const {
return f(*it);
}
iterator_mapped( F f_in, It it_in ):
f(std::move(f_in)),
it(std::move(it_in))
{}
iterator_mapped( iterator_mapped const& ) = default;
iterator_mapped( iterator_mapped && ) = default;
iterator_mapped& operator=( iterator_mapped const& ) = default;
iterator_mapped& operator=( iterator_mapped && ) = default;
iterator_mapped& operator++() {
++it;
return *this;
}
iterator_mapped operator++(int) {
auto copy = *this;
++*this;
return copy;
}
friend bool operator==( iterator_mapped const& lhs, iterator_mapped const& rhs ) {
return lhs.it == rhs.it;
}
friend bool operator!=( iterator_mapped const& lhs, iterator_mapped const& rhs ) {
return !(lhs==rhs);
}
private:
F f;
It it;
};
it is not technically an iterator, but it qualifies for for(:) loops.
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It b, It e ) {
return {std::move(b), std::move(e)};
}
the above is an absolutely minimal iterator range type that can be for(:) iterated.
template<class F, class R>
auto map_range( F&& f, R& r ) {
using std::begin; using std::end;
auto b = begin(r);
auto e = end(r);
using it = iterator_mapped<std::decay_t<F>, decltype(b)>;
return range( it( f, b ), it( f, e ) );
}
note that R& not R&&; taking an rvalue for r here is dangerous.
auto GetStringIterator() const
{
return map_range( [](auto&& pair)->decltype(auto){
return pair.second;
}, m_Items );
}
and done.
Converting this to c++11 is a pain. You have to toss around std::functions in place of lambdas (or write function objects that do the task instead of a lambda), replace decltype(auto) with auto and trailing return types, give the exact type of auto&& arguments to lambdas, etc. You end up with about 25%-50% more code, most of it obscure type chasing.
This is basically what boost::adaptors::map_values does, but this is hand-rolled so you can understand how it works and don't have a boost dependency.
Related
I would like to have a class member variable to be able to switch in between items in a map so that when it is modified, the content of the map is also modified.
Is there any way other than to use a pointer to the content of the map ? Old code only needed only variable, now the new one needs to switch. If I change the variable type, then all functions using this member variable need to be changed. Not complicated, but I would find it ugly to have * in front of it everywhere...
A reference variable cannot be rebound, so how can I achieve this ?
class A
{
std::map<std::string,std::vector<int>> mMyMap;
std::vector<int>& mCurrentVector;
std::vector<int>* mCurrentVectorPointer;
std::vector<int> mDefaultVector;
void setCurrentVector(int iKey);
void addToCurrentVector(int iValue);
}
A::A():
mDefaultVector(std::vector<int>())
mCurrentVector(mDefaultVector)
{
mMyMap["key1"] = std::vector<int>(1,1);
mMyMap["key2"] = std::vector<int>(1,2);
mCurrentVectorPointer = &mMyMap[0];
}
A::setCurrentVector(std::string iKey)
{
if(mMyMap.find(iKey) != mMyMap.end())
{
mCurrentVector = mMyMap[iKey]; //can't change a reference...
mCurrentVectorPointer = &mMyMap[iKey]; //could use pointer, but
}
}
A::addToCurrentVector(int iValue)
{
mCurrentVector.push_back(iValue);
//or
(*mCurrentVectorPointer).push_back(iValue);
//
mCurrentVectorPointer->push_back(iValue);
}
void main()
{
A wClassA();
wClassA.setCurrentVector("key2");
wClassA.addToCurrentVector(3);
wClassA.setCurrentVector("key1");
wClassA.addToCurrentVector(4);
}
mMyMap["key1"] now contains 1,4
mMyMap["key2"] now contains 2,3
You can't reseat a reference once it has been assigned which means you are left with using your other option, a pointer.
As I understand it, you're refactoring some existing code which only used a single vector, whereas now you need a map of vectors.
You're trying to achieve this with minimal modifications, and keeping the interface to the vector the same.
An option would be to use a local reference, assigned from your pointer.
class A
{
using Vector = std::vector<int>;
public:
A()
{
map_["key1"] = std::vector<int>(1,1);
map_["key2"] = std::vector<int>(1,2);
curr_vec_ = &map_["key1"];
}
void setCurrentVector(const std::string& key)
{
if(map_.find(key) != map_.end())
{
curr_vec_ = &map_[key];
}
}
void addToCurrentVector(int val)
{
assert(curr_vec_);
Vector& curr_vec = *curr_vec_; // local reference
curr_vec.push_back(val);
curr_vec[0] = 2;
// etc
}
private:
std::map<std::string, Vector> map_;
Vector* curr_vec_ = nullptr;
}
You may write some wrapper:
#define Return(X) noexcept(noexcept(X)) -> decltype(X) { return X; }
template <typename U>
class MyVectorRef
{
private:
std::vector<U>* vec = nullptr;
public:
explicit MyVectorRef(std::vector<U>& v) : vec(&v) {}
void reset(std::vector<U>& v) {vec = &v;}
// vector interface
auto at(std::size_t i) const Return(vec->at(i))
auto at(std::size_t i) Return(vec->at(i))
auto operator [](std::size_t i) const Return(vec->operator[](i))
auto operator [](std::size_t i) Return(vec->operator[](i))
template <typename ... Ts> auto assign(Ts&&... ts) Return(vec->assign(std::forward<Ts>(ts)...))
auto assign( std::initializer_list<U> ilist ) Return(vec->assign(ilist))
template <typename T> auto push_back(T&& t) const Return(vec->push_back(std::forward<T>(t)))
template <typename T> auto emplace_back(T&& t) const Return(vec->emplace_back(std::forward<T>(t)))
auto begin() const Return(vec->begin())
auto begin() Return(vec->begin())
auto end() const Return(vec->end())
auto end() Return(vec->end())
auto cbegin() const Return(vec->cbegin())
auto cend() const Return(vec->cend())
// ...
};
and then, use it:
class A
{
public:
A() : mCurrentVector(mDefaultVector) {
mMyMap["key1"] = std::vector<int>(1,1);
mMyMap["key2"] = std::vector<int>(1,2);
}
std::map<std::string, std::vector<int>> mMyMap;
std::vector<int> mDefaultVector;
MyVectorRef<int> mCurrentVector;
void setCurrentVector(std::string iKey)
{
auto it = mMyMap.find(iKey);
if (it != mMyMap.end())
{
mCurrentVector.reset(it->second);
}
}
void addToCurrentVector(int iValue)
{
mCurrentVector.push_back(iValue);
}
};
But I think it would be simpler to just create a getter in A and use directly a pointer:
class A
{
public:
A() : mCurrentVector(&mDefaultVector) {
mMyMap["key1"] = std::vector<int>(1,1);
mMyMap["key2"] = std::vector<int>(1,2);
}
std::map<std::string, std::vector<int>> mMyMap;
std::vector<int> mDefaultVector;
std::vector<int>* mCurrentVector;
std::vector<int>& GeCurrentVector() { return *mCurrentVector; }
void setCurrentVector(std::string iKey)
{
auto it = mMyMap.find(iKey);
if (it != mMyMap.end())
{
mCurrentVector = &it->second;
}
}
void addToCurrentVector(int iValue)
{
GeCurrentVector().push_back(iValue);
}
};
I have a container class like this:
class A {
public:
// assuming all the other operators used below
// have been defined.
// ...
A operator()(const A& a) const {
A r(a.size());
for (int i = 0;i < a.size();++i) r[i] = data[a[i]];
return r;
}
private:
std::vector<int> data;
};
so I can do things like this:
A a, b, c;
// ... initialize data here...
c = a(b); // I can index a by b
Now I want to make the indexed container a(b) assignable, e.g.
a(b) = c;
for example, if a is {1, 2, 3, 4} and b is {0,2}, c is {0,0}, the above line should give me a = {0,2,0,4}. because a indexed by {0,2} is {1,3} in a, and set them to c {0,0} will give me this.
how to do that?
You can't do it directly with the type A. You would need an intermediate type that maps or references back to the object. As a simple, non-optimal example:
#include <functional>
#include <vector>
class A
{
public:
class Ref
{
private:
friend class A;
std::vector<std::reference_wrapper<int>> refs;
Ref( A & obj, A & idx )
{
refs.reserve( idx.data.size() );
auto val_it = obj.data.begin();
for( auto i : idx.data ) {
ref.emplace_back( std::ref( data[i] ) );
}
}
public:
Ref & operator=( const A & obj )
{
auto obj_it = obj.data.begin();
for( auto ref : refs ) {
ref.get() = *obj_it++;
}
return this;
}
};
// ...
Ref operator()( const A & idx )
{
return Ref( *this, idx );
}
private:
std::vector<int> data;
};
And then things start getting fiddly, because you'll want to be able to convert between these reference-based views and the original type.
The easy way is to keep your const operator as you implemented (note that my one is non-const): i.e. it still returns type A, which makes sense. But you will probably want to be able to build a new A from a A::Ref, and so you'd want a constructor like:
A::A( const Ref & r )
{
data.reserve( r.refs.size() );
auto r_it = r.refs.begin();
for( auto ref : r.refs ) {
data.push_back( ref.get() );
}
}
Anyway, that's an easy concept to get you started, and something to play around with.
An array view is a view into a contiguous buffer of T:
template<class T>
struct array_view {
T* b=0;
T* e=0;
T* begin() const { return b; }
T* end() const { return e; }
std::size_t size() const { return end()-begin(); }
bool empty() const { return end()==begin(); }
T& operator[](std::size_t i)const { return begin()[i]; }
array_view( array_view const& ) = default;
array_view& operator=( array_view const& ) = default;
array_view() = default;
array_view( T* s, T* f ):b(s),e(f) {}
array_view( T* s, std::size_t n ):array_view(s, s+n) {}
};
template<class Src>
array_view< std::remove_reference_t<
decltype(*(std::declval<Src&>().data()))
> >
make_array_view( Src& src ) {
return {src.data(), src.size()};
}
(To make it better, do general "range" upgrades. const in the above case refers to "changing what range is viewed" not the contents -- if you want const contents, make an array_view<const T>. Another improvement would be to hvae constructors that do what make_array_view does, also supporting initializer_list, rvalues, and raw C arrays).
Given that, here is a permuted view of an array view of T.
First a permutation is a function from a bounded set of size_t to a different set of size_t.
struct permutation {
std::function< std::size_t(std::size_t) > mapping;
std::size_t count = 0;
std::size_t size() const { return count; }
permutation( std::function< std::size_t(std::size_t) > m, std::size_t c):
mapping(std::move(m)),
count(c)
{}
std::size_t operator()( std::size_t i )const {
return mapping(i);
}
};
This isn't the safest, because we don't check that the output range is reasonable.
A factory function:
template<class T>
permutation make_permutation_from( T src ) {
auto size = src.size();
return {
[src = std::move(src)]( std::size_t in ) {
return src[in];
},
size
};
}
// optimization
permutation make_permutation_from( permutation src ) {
return src;
}
and one to compose two permutations. The validity of the size field is not checked.
// if they don't align, we are screwed, but...
permutation chain_permutation( permutation first, permutation second ) {
auto first_size = first.size();
return {
[first=std::move(first), second=std::move(second)](std::size_t i){
return second(first(i));
},
first_size
};
}
This leads us to the permuted view, which is a view of an array_view that permutes indexes.
template<class T>
struct permuted_view {
array_view<T> source;
permutation permute;
std::size_t size() const { return permute.size(); }
T& operator[]( std::size_t i ) const {
return source[ permute(i) ];
}
template<class Src>
void assign_from( Src const& src ) {
if (src.size() != size()) exit(-1);
for (std::size_t i = 0; i < size(); ++i)
(*this)[i] = src[i];
}
void operator=( permuted_view const& src ) { assign_from(src); }
template<class U>
void operator=( U const& src ) { assign_from(src); }
template<class U,
std::enable_if_t< !std::is_integral<U>{}, int> =0
>
permuted_view<T> operator[]( U u )const {
return {
source,
chain_permutation( make_permutation_from(std::move(u)), permute )
};
}
};
Now a permuted_view<int> is a permutation on an array of int.
Note that permuted_view doesn't own anything. It just refers to someone else's storage. Ownership is something you'll have to work out for yourself. Maybe through smart pointers, or some other means.
Efficient libraries for this purpose probably have copy-on-write sparse arrays. To get this, it is a lot of work, or you should find a library like Eigen.
live example.
You'll want to add an iterator and begin/end to permuted_view. I'd make the iterator store a pointer to the view and an index, and have it use operator[] on the view when you dereference.
If you refactor array_view<T> into range_view<Iterator> with a specialization or subclass for T* iterators, you can then refactor range_view<Iterator> into range_helper<Iterator, Derived> with CRTP. Then reuse range_helper for permuted_view and for range_view, and range_view for array_view. But that is getting a bit off the reservation.
Various libraries, including Rangesv3 and boost and C++17 std and C++20 std::experimental and the like, have either written these types or make writing these types easier.
I have a vector that looks like this:
std::vector<std::vector<MyClass>> myVector;
And I would like to access its elements through iterators as if it was an unidimensional vector:
for (auto& x : myVector)
{
foo(x); // x is an object of type MyClass
}
(i.e. the fact that there are multiple dimensions is transparent to whoever loops through myVector)
I have an idea of how this should be done, have a custom iterator implementation that saves current indexes so that when one of the vectors has no more elements, it resets one of the indexes and increments the other so that it can start iterating through the next vector and so on. But I have been trying to code this idea but can't seem to get this working. Does anyone have any idea of how I can possibly achieve this? Or even better, if there's any open-source project that has a similar implementation?
Thanks.
It's totally possible to define your own iterator to hide all the details of iterating through a vector of vector, I wrote some code to give you the idea, mind that it should require more checks but it basically works and give you the idea.
You just need to write the required operations to make it work in other code like an opaque iterator.
template <typename T>
struct vector2d
{
public:
class iterator
{
public:
using vector_type = std::vector<std::vector<T>>;
using first_level_iterator = typename std::vector<std::vector<T>>::iterator;
using second_level_iterator = typename std::vector<T>::iterator;
private:
vector_type& data;
first_level_iterator fit;
second_level_iterator sit;
public:
iterator(vector_type& data, bool begin) : data(data)
{
if (begin)
{
fit = data.begin();
sit = fit->begin();
}
else
{
fit = data.end();
}
}
inline bool operator!=(const iterator& other) const { return fit != other.fit || (fit != data.end() && sit != other.sit); }
inline const iterator& operator++() {
// don't go past end
if (fit == data.end())
return *this;
// increment inner iterator
++sit;
// if we reached the end of inner vector
if (sit == fit->end())
{
// go to next vector
++fit;
// if we reached end then don't reset sit since it would be UB
if (fit != data.end())
sit = fit->begin();
}
return *this;
}
T& operator*() const { return *sit; }
};
public:
std::vector<std::vector<T>> data;
iterator begin() { return iterator(this->data, true); }
iterator end() { return iterator(this->data, false); }
};
A small test:
int main() {
vector2d<int> data;
data.data.push_back(vector<int>());
data.data.push_back(vector<int>());
data.data.push_back(vector<int>());
for (int i = 1; i < 5; ++i)
{
data.data[0].push_back(i);
data.data[1].push_back(i*2);
data.data[2].push_back(i*3);
}
for (auto i : data)
{
cout << i << endl;
}
return 0;
}
The behavior is rather simple but you must make sure that it's always consistent for all the edge cases.
A pretty minimal range type:
template<class It>
struct range_t {
private:
It b, e;
public:
It begin() const { return b; }
It end() const { return e; }
decltype(auto) front() const { return *b; }
decltype(auto) back() const { return *std::prev(e); }
bool empty() const { return b==e; }
range_t without_front( std::size_t n = 1 ) const {
auto r = *this;
std::advance(r.b,n);
return r;
}
range_t without_back( std::size_t n = 1 ) const {
auto r = *this;
std::advance(r.e,std::ptrdiff_t(-n));
return r;
}
range_t(It s, It f):b(std::move(s)), e(std::move(f)) {}
range_t():b(), e() {}
};
template<class It>
range_t<It> range( It b, It e ) {
return {std::move(b), std::move(e)};
}
Doing this task is far easier with ranges than with iterators.
template<class Outer, class Inner>
struct stacked_range_t {
range_t<Outer> outer;
stacked_range_t()=default;
stacked_range_t( range_t<Outer> o ):outer(std::move(o)) {}
struct iterator {
private:
range_t<Outer> outer;
range_t<Inner> inner;
public:
iterator(
range_t<Outer> o,
range_t<Inner> i
):outer(std::move(o)), inner(std::move(i)) {}
iterator()=default;
friend auto mytie(iterator const& it) {
return std::tie( it.outer.begin(), it.inner.begin(), it.inner.end() );
}
friend bool operator==(iterator const& lhs, iterator const& rhs) {
return mytie(lhs)==mytie(rhs);
}
friend bool operator!=(iterator const& lhs, iterator const& rhs) {
return mytie(lhs)==mytie(rhs);
}
using difference_type = std::ptrdiff_t;
using value_type = typename std::iterator_traits<Inner>::value_type;
using pointer = typename std::iterator_traits<Inner>::pointer;
using reference = typename std::iterator_traits<Inner>::reference;
using iterator_category = std::input_iterator_tag;
reference operator*() const {
return *inner.begin();
}
pointer operator->() const {
return inner.begin().operator->();
}
iterator& operator++() {
using std::begin; using std::end;
inner = inner.without_front();
while (inner.empty())
{
outer = outer.without_front();
if (!outer.empty())
inner = range( begin(outer.front()), end(outer.front()) );
}
return *this;
}
iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
};
iterator end() const {
return { range( outer.end(), outer.end() ), {} };
}
// a bit tricky:
iterator begin() const {
if (outer.empty()) return end();
auto rout = outer;
while( !rout.empty() ) {
using std::begin; using std::end;
auto rin = range( begin(rout.front()), end(rout.front()) );
if (!rin.empty())
return {std::move(rout), std::move(rin)};
rout = rout.without_front();
}
return end();
}
};
and a function to create it:
template<class Range>
auto make_stacked_range(Range& r) {
using std::begin; using std::end;
using Outer = decltype(begin(r));
using Inner = decltype(begin(*begin(r));
return stacked_range_t<Outer, Inner>{
{begin(r), end(r)}
};
}
there probably are typos. Use of C++1z features can be worked around with overly annoying decltype expressions and helper traits classes.
Relies on the iterators being trivially constructible, and such trivially constructed iterators are equal.
try to use template recursion,e.g.:
#include <stdio.h>
#include <vector>
template <typename V>
void f(V& v){
for(auto& e : v){
f(e);
}
printf("\n");
}
template <>
void f(int& v){
printf("%d ",v);
}
int main(){
std::vector<int> v1={1,2};
f(v1);
std::vector<std::vector<int> > v2={{3,4},{5,6,7}};
f(v2);
return 0;
};
With range/v3:
for (auto& x : myVector | ranges::view::join)
{
foo(x); // x is an object of type MyClass&
}
Demo
This question already has answers here:
How to make my custom type to work with "range-based for loops"?
(10 answers)
Closed 7 years ago.
I have a class like this:
class Foo {
private:
int a,b,c,d;
char bar;
double m,n
public:
//constructors here
};
I wanna allow range-for loop on my class, e.g.
Foo foo {/*...*/};
for(auto& f : foo) {
//f will be a specific order such as c,b,d,(int)m,(int)bar,a,(int)n
}
How can I achieve this? I was looking at iterator but don't know what are the requirements for a range-for loop. (Please don't ask me to use array or STL type)
The loop is defined to be equivalent to:
for ( auto __begin = <begin-expr>,
__end = <end-expr>;
__begin != __end;
++__begin ) {
auto& f = *__begin;
// loop body
}
where <begin-expr> is foo.begin(), or begin(foo) if there isn't a suitable member function, and likewise for <end-expr>. (This is a simplification of the specification in C++11 6.5.4, for this particular case where the range is a lvalue of class type).
So you need to define an iterator type that supports pre-increment ++it, dereference *it and comparison i1 != i2; and either
give foo public member functions begin() and end(); or
define non-member functions begin(foo) and end(foo), in the same namespace as foo so that they can be found by argument-dependent lookup.
This seems fairly un-C++-like, and rather prone to breakage. What if the iteration order is changed (accidentally or not) during some update in the future? Clients relying on a specific order will break.
All that said if you wish to support this all you have to do is implement your own iterator and provide begin/end methods (or free functions with those names) to provide access. Then the iterator takes care of remembering which attribute it's currently looking at and provides it when dereferenced.
Here is a basic framework I came up with:
#include <iterator>
struct Foo;
template<typename Type>
struct MemberPtrBase {
virtual ~MemberPtrBase() { }
virtual Type get() const = 0;
virtual MemberPtrBase & set(Type const &) = 0;
};
template<typename Class, typename RealType, typename CommonType>
struct MemberPtr : MemberPtrBase<CommonType> {
public:
MemberPtr(Class * object, RealType(Class::*member))
: m_object(object), m_ptr(member)
{ }
CommonType get() const {
return m_object->*m_ptr;
}
MemberPtr & set(CommonType const & val) {
m_object->*m_ptr = val;
return *this;
}
MemberPtr & operator=(RealType const & val) {
return set(val);
}
operator CommonType() const {
return get();
}
private:
Class * m_object;
RealType (Class::*m_ptr);
};
template<typename Class, typename... Types>
struct MemberIterator {
public:
using CommonType = typename std::common_type<Types...>::type;
public:
MemberIterator(Class & obj, std::size_t idx, Types(Class::*...member))
: m_object(obj), m_index(idx), m_members { new MemberPtr<Class, Types, CommonType>(&obj, member)... }
{ }
MemberPtrBase<CommonType> & operator*() const {
return *m_members[m_index];
}
bool operator==(MemberIterator const & it) const {
return (&m_object == &it.m_object) && (m_index == it.m_index);
}
bool operator!=(MemberIterator const & it) const {
return (&m_object != &it.m_object) || (m_index != it.m_index);
}
MemberIterator & operator++() {
++m_index;
return *this;
}
private:
Class & m_object;
std::size_t m_index;
MemberPtrBase<CommonType> * m_members[sizeof...(Types)];
};
struct Foo {
public:
using iterator = MemberIterator<Foo, int, int, int, int>;
public:
Foo(int a, int b, int c, int d)
: m_a(a), m_b(b), m_c(c), m_d(d)
{ }
iterator begin() {
return iterator(*this, 0, &Foo::m_b, &Foo::m_d, &Foo::m_c, &Foo::m_a);
}
iterator end() {
return iterator(*this, 4, &Foo::m_b, &Foo::m_d, &Foo::m_c, &Foo::m_a);
}
private:
int m_a, m_b, m_c, m_d;
};
If you have a basic understanding of variadic templates, I think the code is self-explanatory.
Usage is simple:
#include <iostream>
int main(int argc, char ** argv) {
Foo foo { 1, 2, 3, 4 };
for(auto & mem : foo) {
std::cout << mem.get() << std::endl;
mem.set(3);
}
for(auto & mem : foo) {
std::cout << mem.get() << std::endl;
}
}
A POC can be found on ideone
I'd like a generic method for retrieving the data from a vector.
I have a the following class and vector:
class myClass
{
public:
myClass(int myX, float myZ, std::string myFoo)
: x ( myX )
, z ( myZ )
, foo ( myFoo )
{
}
myClass()
{
}
int x;
float z;
std::string foo;
} ;
std::vector < myClass > myVector;
(The complete code can be seen here: http://codepad.org/iDD1Wme5 )
In this example I would like to be able to retrieve objects in the vector based on the "z" or "foo" members without having to write another 2 functions similar to "FindDataById".
Is that possible?
You can use a template and pointer to member.
typedef vector<myClass> myVector;
template<typename T>
bool FindDataById(const T &id, T myClass::* idMember, myClass &theClass,
const myVector &theVector)
{
for(myVector::const_iterator itr = theVector.begin(); itr != myVector.end();
++itr){
if((*itr).*idMember == id){
theClass = *itr;
return true;
}
return false;
}
Then call using, e.g.,
FindDataById(string("name"), &myClass::foo, theClass, theVector)
FindDataById(5, &myClass::x, theClass, theVector)
FindDataById(5.25f, &myClass::z, theClass, theVector)
Or, go with the find_if idea:
template<typename T>
struct Finder {
T val_;
T myClass::* idMember_;
Finder(T val, T myClass::* idMember) : val_(val), idMember_(idMember) {}
bool operator()(const myClass &obj) { return obj.*idMember_ == val_; }
};
And use:
find_if(theVector.begin(), theVector.end(), Finder<string>("name", &myClass::foo))
find_if(theVector.begin(), theVector.end(), Finder<int>(5, &myClass::x))
find_if(theVector.begin(), theVector.end(), Finder<float>(3.25f, &myClass::z))
See the answer of MSalters for a way to deduce the template argument automatically.
std::find_if has already been suggested, but without a code sample, so here's a more detailed version:
Define two functors to identify the object you're interested in:
struct z_equals {
z_equals(float z) : z(z) {}
bool operator()(const myClass& obj)
return z == obj.z;
}
float z;
};
struct foo_equals {
foo_equals(const std::string& foo) : foo(foo) {}
bool operator()(const myClass& obj)
return foo == obj.foo;
}
const std::string& foo;
};
And now, to search for elements where z == 42.0f, or foo == "hello world":
std::find_if(myVector.begin(), myVector.end(), z_equals(42.0f));
std::find_if(myVector.begin(), myVector.end(), foo_equals("hello world"));
You can use functors and pass it to your lookup method. That I mean is, define class which will overload bool operator( vectorElement element) and within this operator you will choose method how do you want to lookup the values.
template <typename T>
class ILookUp
{
bool operator( vector<T> elem)
{
if (elem == something)
return true;
false;
}
};
class VectorStorage
{
std::vector<Elements> lookup( ILookUp<Elements> lookup)
{
.....
if ( lookup(elem))
{
//add element to vector or whatever.
}
.....
return result;
}
.....
}
It might be worth taking a look at std::find defined in algorithm and boost::lambda
Without lambda's you'd need to write some predicates, or at least instantiate them:
template
struct member_select : public std::unary_function
{
T t;
T U::* m_u;
member_select(T const& t, T U::* m_u) : t(t), m_u(m_u) {}
bool operator()(U const& u) const { return u.*m_u == t; }
};
template
member_select make_member_select(T const& t, T U::* m_u)
{
return member_select(t, m_u);
}
Use: std::find_if(..., make_member_select("x", &myClass::foo));