How can I use a vector wrapper class enclosed in another vector?

Consider a free function from the library of the third part, which expects the argument std::vector : void foo( std::vector<sometype>& );

Now I am writing a wrapper around this type to add member functions. To be able to use foo() with this type, I add an access function.

 class Wrapper { private: std::vector<sometype> _data; public: std::vector<sometype>& data() { return _data; } const std::vector<sometype>& data() const { return _data; } //... other stuff }; 

That way I can still use foo() :

 Wrapper a; foo( a.data() ); 

But now consider another function that expects a vector of sometype vectors ( edit: and which adds elements to this vector ):

 void bar( std::vector<std::vector<sometype>>& ); 

But the data type I have is std::vector<Wrapper> vec;

Can I use my shell type to call bar() ? I want to do the following:

  std::vector<Wrapper> vec; bar( ??? ); 

What I want to avoid is the first call to bar() with the required type, and then I have to copy the elements one by one into my vector<Wrapper> .

First I would say no, but maybe there is some kind of smart solution?

Edit2 : to give an example, consider the following game game for bar() with the root type int :

 void bar( std::vector<std::vector<int>>& vv ) { std::vector<int> v1 = { 1,2,3 }; std::vector<int> v2 = { 4,5,6 }; vv.push_back(v1); vv.push_back(v2); } 
+8
c ++ vector stdvector wrapper
source share
4 answers

[Edited after new comments requiring adding elements to the bar function] A possible solution would be to save std::vector<std::vector<sometype>> to use the function and just work with the VectorAccessor object related to real vectors

 #include <iostream> #include <vector> struct sometype { int value; sometype(int v) : value(v) {} }; void bar(std::vector<std::vector<sometype>>& par) { std::cout << "bar() - Before adding new elements:" << std::endl; for (auto& subvec : par) { std::cout << "Subvector: {"; for (auto& sometypeItem : subvec) { std::cout << sometypeItem.value << " "; } std::cout << "};" << std::endl; } std::vector<sometype> newItem = {32, 33}; par.emplace_back(newItem); } class VectorAccessor { std::vector<std::vector<sometype>>& m_vec; public: VectorAccessor(std::vector<std::vector<sometype>>& v) : m_vec(v) {} template<typename V> void addVector(V&& vec) { static_assert(std::is_same<typename std::remove_reference<V>::type, std::vector<sometype>>::value, "Not the right type"); m_vec.emplace_back(std::forward<V>(vec)); } std::vector<sometype> accessVector(size_t index) { return m_vec[index]; } }; int main(int argc, char ** argv) { std::vector<std::vector<sometype>> vec; VectorAccessor vAcc(vec); // Add an element through the vector accessor std::vector<sometype> firstVector = {42}; firstVector.emplace_back(52); vAcc.addVector(firstVector); // Call bar and add a few elements bar(vec); // Now access stuff with the usual wrapper std::cout << "Elements added by bar:" << std::endl; std::cout << "Subvector: {"; for (auto& sometypeItem : vAcc.accessVector(1)) { std::cout << sometypeItem.value << " "; } std::cout << "};" << std::endl; return 0; } 

Example

+3
source share

instead of std::vector<Wrapper> vec;

using

 std::vector< std::vector<sometype> > vec; 

Anyway you can insert Wrapper objects in vec

 vec.push_back(a.data()); 

and then call bar(vec);

+1
source share

Out of the box, calling a function with vector<vector<something> will not work with vector<Wrapper> because their type is different and the compiler explicitly expects the first.

I do not think that in C ++ this form of type substitution could work.

Bypass

There is a workaround there: you can use transformations in your own code to make the magic happen.

Let me explain.

If the function you intend to use takes vector<vector<something>> , in C ++ you basically need to give it vector<vector<something>> . Thus, you cannot create your vector as vector<Wrapper> and not convert it to vector<vector<something>> .

On the other hand, you can

  • use vector<vector<something> , into which you will push Wrapper instances (using implicit conversion).
  • If you need the Wrapper functional function, you can convert your vector<something> using the conversion constructor.

Take this example:

 #include <iostream> #include <vector> using namespace std; //Templated class wrapper. It does not have to be templated though. template<typename T> class Wrapper{ private: //Here is our inner vector. vector<T> vect; public: //here is our implicit convertion operator : operator vector<T>& () const {return this->vect;} //A function so that we can push some stuff in it void push(T elem){ this->vect.push_back(elem); } //here is some additional functionnality in top of vector; void print(){ int i = 0; for(i=0;i<this->vect.size();i++){ cout << vect[i] << " "; } cout << endl; } //this is our very simple conversion constructor Wrapper<T>(vector<T> vect){ this->vect = vect; } //we still need a normal constructor Wrapper<T>(){} }; //A function that takes a vector of vectors. vector<int> concat(vector<vector<int>> vectors){ int i = 0,j=0; vector<int> result; for(i=0;i<vectors.size();i++){ for(j=0;j<vectors[i].size();j++){ result.push_back(vectors[i][j]); } } return result; } int main() { //Let create an instance of Wrapper and fill it. Wrapper<int>ex; ex.push(1); ex.push(2); //And yet another one Wrapper<int>ex2; ex2.push(5); ex2.push(6); //Here we create precisely what the 'concat' function wants: //namely a vector<vector<int>>. vector<vector<int>> vectors; //you can push Wrappers in it, since the conversion will take place. vectors.push_back(ex); vectors.push_back(ex2); //this function call will be successful, since the type of //vectors is vector<vector<int>> vector<int> res = concat(vectors); //Now if you want to use the wrapper functionnality on any //vector<int>, just convert it on-demand. //The constructor is extra light-weight in terms of computing power //as you can see above. Wrapper<int>(res).print(); Wrapper<int>(vectors[0]).print(); } 

PS The push_back function will copy the element, so if your function really changes your vector, it will not be reflected on the Wrapper, since this is a copy of its internal vector that has been changed. Using real vector<something> and push_back will lead to the same behavior.

+1
source share

Ok, so I came up with something that works, although there may be some problems. The idea is to wrap a vector of vectors in some global shell, and then the original shell, which accesses the data inside it using pointers.

Say the following bar() toy:

 void bar(std::vector<std::vector<int>>& par) { std::vector<int> v1 = { 1,2,3 }; par.push_back(v1); } 

Two wrappers:

 struct GlobalWrapper { std::vector<std::vector<int>> _data; size_t size() const { return _data.size(); } std::vector<int>& Get( size_t i ) { return _data[i]; } const std::vector<int>& Get( size_t i ) const { return _data[i]; } }; struct Wrapper { std::vector<int>* _data; void DoSomething() const { cout << "values: "; std::copy( _data->begin(), _data->end(), std::ostream_iterator<int>(std::cout, " ")); } Wrapper( std::vector<int>& value ) : _data(&value) { } }; 

And the test program:

 int main(int argc, char ** argv) { GlobalWrapper gw; cout << "size before=" << gw.size() << endl; bar( gw._data ); cout << "size after=" << gw.size() << endl; Wrapper w = gw.Get(0); // get first element and do something with it w.DoSomething(); return 0; } 

One problem remains: data ownership. Perhaps some smart pointers are needed.

Running code here .

0
source share

All Articles