Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference to a partial segment of a vector?

I have a black box C++ function which I don't have access to its source code:

void blackbox(vector<int> &input);

This function modifies the element of the input vector in an unknown manner.

The problem I have now is that I want to apply the black box function only for a partial segment of a vector, for example, the last 500 elements of a vector. So, this is the routine that I wrote to attain this goal:

vector<int> foo (5,1000);
vector<int> bar (foo.end()-500,foo.end());

blackbox(bar);

swap_ranges(foo.end()-500,foo.end(),bar.begin());

This code may work, but is there a better way to do this?

It would be good if I can define a vector reference only for a segment of an existing vector, instead of creating a copy. I am not so comfortable with the copying and swapping parts in the above code; since this routine is invoked so frequently, I think the repeated copying and swapping slows down the code. If I knew the exact operations done by the block box, I would rewrite the function so that it takes vector iterators as the input arguments. Unfortunately, this is not possible at the moment.

like image 252
user68390 Avatar asked Feb 20 '15 04:02

user68390


People also ask

How do you find a sub vector in C++?

Getting a subvector from a vector in C++ auto last = v. begin() + n + 1. Declare a variable vector of vector type. Pass the value of first and last position of vector.


1 Answers

There's no well-defined way to achieve this functionality. With huge caveats and warnings, it can (for one GCC version at least) be hacked as below, or you could perhaps write something with better defined behaviour but based on your compiler's current std::vector implementation....

So... hacked. This will not work if insert/erase/resize/reserve/clear/push_back or any other operation affecting the overall vector is performed. It may not be portable / continue working / work with all optimisation levels / work on Tuesdays / use at own risk etc.. It depends on the empty base class optimisation.

You need a custom allocator but there's a catch: the allocator can't have any state or it'll change the binary layout of the vector object, so we end up with this:

#include <iostream>
#include <vector>

template <typename Container>  // easy to get this working...
void f(Container& v)
{
    std::cout << "f() v.data() " << v.data() << ", v.size() " << v.size() << '\n';
    for (int& n : v) n += 10;
}

void g(std::vector<int>& v)  // hard to get this working...
{
    std::cout << "g() v.data() " << v.data() << ", v.size() " << v.size() << '\n';
    for (int& n : v) n += 100;
}

int* p_;   // ouch: can't be a member without changing vector<> memory layout


struct My_alloc : std::allocator<int>
{
    // all no-ops except allocate() which returns the constructor argument...

    My_alloc(int* p) { p_ = p; }

    template <class U, class... Args>
    void construct(U* p, Args&&... args) { std::cout << "My_alloc::construct(U* " << p << ")\n"; }

    template <class U> void destroy(U* p) { std::cout << "My_alloc::destroy(U* " << p << ")\n"; }

    pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
    {
        std::cout << "My_alloc::allocate() return " << p_ << "\n";
        return p_;
    }
    void deallocate(pointer p, size_type n) { std::cout << "deallocate\n"; }

    template <typename U>
    struct rebind { typedef My_alloc other; };
};

int main()
{
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    std::cout << "main() v.data() " << v.data() << '\n';
    My_alloc my_alloc(&v[3]);  // first element to "take over"
    std::vector<int, My_alloc> w(3, my_alloc);  // num elements to "take over"
    f(w);
    g(reinterpret_cast<std::vector<int>&>(w));
    for (int n : v) std::cout << n << ' ';
    std::cout << '\n';
    std::cout << "sizeof v " << sizeof v << ", sizeof w " << sizeof w << '\n';
}

Output:

main() v.data() 0x9d76008
My_alloc::allocate() return 0x9d76014
My_alloc::construct(U* 0x9d76014)
My_alloc::construct(U* 0x9d76018)
My_alloc::construct(U* 0x9d7601c)
f() v.data() 0x9d76014, v.size() 3
g() v.data() 0x9d76014, v.size() 3
0 1 2 113 114 115 6 7 8 9 
sizeof v 12, sizeof w 12
My_alloc::destroy(U* 0x9d76014)
My_alloc::destroy(U* 0x9d76018)
My_alloc::destroy(U* 0x9d7601c)
deallocate

See it run here

like image 60
5 revs Avatar answered Nov 15 '22 00:11

5 revs