3

Is it possible to do this without creating new data structure? Suppose we have

struct Span{
    int from;
    int to;
}
vector<Span> s;

We want to get an integer vector from s directly, by casting

vector<Span> s;

to

vector<int> s;

so we could remove/change some "from", "to" elements, then cast it back to

vector<Span> s;  
aaronqli
  • 790
  • 9
  • 26
  • 7
    Your description makes little sense. This sounds like a case of the [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Exactly what are you trying to do? Can you show an example? – R. Martinho Fernandes Jan 31 '12 at 02:20
  • It is hard to guess what you want. Can you provide an example? – MAK Jan 31 '12 at 02:21
  • @R.MartinhoFernandes: I think the poster wants to change a `vector` into a `vector` and then get some `other`s out of this `vector` before changing it back to a `vector`. I don't think this is possible, though. I'm quite sure that, at least in Java, it would be a very awry operation and I don't think you could do it in C++. – blahman Jan 31 '12 at 02:36
  • +1 because it’s an interesting question even if it’s probably a bad idea. – Jon Purdy Jan 31 '12 at 03:54
  • @R.MartinhoFernandes I writing an efficient algorithm to merge a span into an existing span array. I have already implemented this algorithm but I make a copy of the array. This is bad because the span array could be large, and certainly there is a way not to do this. It would be nice if we can avoid creating complex data structures hack the STL vector and C++ pointer so the code would be short and neat. – aaronqli Jan 31 '12 at 03:58
  • 2
    @Polymorpher: First, you should have asked "I have this algorithm to do such and such, but it makes a copy. How can I do it in-place?". Second, `vector` is not a complex data structure. And third, *why do you think you need to "hack" anything* to do this? – R. Martinho Fernandes Jan 31 '12 at 04:22
  • @R.MartinhoFernandes You are right - I should probably asked that instead. But there are many ways of doing it in-place, i.e interval trees, but they are neither space efficient nor necessary in this algorithm. What truly needed is a dynamic array, and I think in C++ STL vector is best for that purpose. The reason I didn't mention all above is I am trying not to distract people / waste their time or lead them to wrong direction. – aaronqli Jan 31 '12 at 05:29

3 Answers3

2

This is not really a good idea, but I'll show you how.

You can get a raw pointer to the integer this way:

int * myPointer2 = (int*)&(s[0]);

but this is really bad practice because you can't guarantee that the span structure doesn't have any padding, so while it might work fine for me and you today we can't say much for other systems.

#include <iostream>
#include <vector>


struct Span{
    int from;
    int to;
};


int main()
{

    std::vector<Span> s;

    Span a = { 1, 2};
    Span b = {2, 9};
    Span c = {10, 14};

    s.push_back(a);
    s.push_back(b);
    s.push_back(c);


    int * myPointer = (int*)&(s[0]);

    for(int k = 0; k < 6; k++)
    {
        std::cout << myPointer[k] << std::endl;
    }

    return 0;
}

As I said, that hard reinterpret cast will often work but is very dangerous and lacks the cross-platform guarantees you normally expect from C/C++.

The next worse thing is this, that will actually do what you asked but you should never do. This is the sort of code you could get fired for:

// Baaaad mojo here: turn a vector<span> into a vector<int>:
std::vector<int> * pis = (std::vector<int>*)&s;

for ( std::vector<int>::iterator It = pis->begin(); It != pis->end(); It++ )
        std::cout << *It << std::endl;

Notice how I'm using a pointer to vector and pointing to the address of the vector object s. My hope is that the internals of both vectors are the same and I can use them just like that. For me, this works and while the standard templates may luckily require this to be the case, it is not generally so for templated classes (see such things as padding and template specialization).

Consider instead copying out an array (see ref 2 below) or just using s1.from and s[2].to.

Related Reading:

  1. Are std::vector elements guaranteed to be contiguous?
  2. How to convert vector to array in C++
Community
  • 1
  • 1
Steve
  • 3,957
  • 2
  • 26
  • 50
  • +1: I mucked around in my VBox trying this but I didn't think of casting to pointers and then back. This kind of trick seems more like something you'd find/hack out in C, and as you pointed out it's really quite dangerous. A really good answer ^^ – blahman Jan 31 '12 at 03:14
  • 1
    I think a "better" idea would be to cast to a pointer to array of 2 elements: `reinterpret_cast(&s[0]);`, and access the individual members with the subscript operator. However, I think this is stll horrifically undefined behaviour right there. – Xeo Jan 31 '12 at 03:19
  • @R.MartinhoFernandes Yes, Windows 7 64bit, Intel 2600k, Visual Studio 2008 9.0.30729.1 SP – Steve Jan 31 '12 at 03:20
  • Ugly casts aren't necessary. Replace the first line with `int * myPointer2 = &(s[0].from);` In practice, it'll work on any system where `sizeof (Span) = 2 * sizeof (int)`, because that makes all the pointer math come out the same. – Ben Voigt Jan 31 '12 at 03:40
  • Thanks. Very good explanation. This is almost what I am looking for - A dirty trick to hack C++ STL and make it work. The only problem is this hack doesn't automatically correct internal values/members of the vector (if some elements are inserted/removed after the vector is cast to vector), so we might run into some problem later on. Making a copy of the vector will slow down the code too much. I guess the best way is still to create a new data structure - a customized dynamic array. – aaronqli Jan 31 '12 at 03:52
  • @Polymorpher If element removal is causing speed issues have you considered using a list instead? http://www.cplusplus.com/reference/stl/list/ – Steve Jan 31 '12 at 04:15
  • @Steve Removal doesn't cause much speed issue in the rest of the code (removal will only be called once later on). List doesn't allow random access so it would be bad for binary search. I think the main issue with casting is members of the vector itself - iterators, size, capacity, etc. - are not changed automatically (maybe it is implemented within STL? I will test later on). So after doing something on the int vector and casting it back to span vector, everything will be messed up. I will have a look at the implementation of STL vector to see if I can fix it. – aaronqli Jan 31 '12 at 05:22
2

If sizeof(Span) == sizeof(int) * 2 (that is, Span has no padding), then you can safely use reinterpret_cast<int*>(&v[0]) to get a pointer to array of int that you can iterate over. You can guarantee no-padding structures on a per-compiler basis, with __attribute__((__packed__)) in GCC and #pragma pack in Visual Studio.

However, there is a way that is guaranteed by the standard. Define Span like so:

struct Span {
    int endpoints[2];
};

endpoints[0] and endpoints[1] are required to be contiguous. Add some from() and to() accessors for your convenience, if you like, but now you can use reinterpret_cast<int*>(&v[0]) to your heart’s content.

But if you’re going to be doing a lot of this pointer-munging, you might want to make your own vector-like data structure that is more amenable to this treatment—one that offers more safety guarantees so you can avoid shot feet.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
1

Disclaimer: I have absolutely no idea about what you are trying to do. I am simply making educated guesses and showing possible solutions based on that. Hopefully I'll guess one right and you won't have to do crazy shenanigans with stupid casts.

If you want to remove a certain element from the vector, all you need to do is find it and remove it, using the erase function. You need an iterator to your element, and obtaining that iterator depends on what you know about the element in question. Given std::vector<Span> v;:

  • If you know its index:

    v.erase(v.begin() + idx);
    
  • If you have an object that is equal to the one you're looking for:

    Span doppelganger;
    v.erase(std::find(v.begin(), v.end(), doppelganger));
    
  • If you have an object that is equal to what you're looking for but want to remove all equal elements, you need the erase-remove idiom:

    Span doppelganger;
    v.erase(std::remove(v.begin(), v.end(), doppelganger)),
            v.end());
    
  • If you have some criterion to select the element:

    v.erase(std::find(v.begin(), v.end(),
                      [](Span const& s) { return s.from == 0; }));
    
    // in C++03 you need a separate function for the criterion
    bool starts_from_zero(Span const& s) { return s.from == 0; }
    
    v.erase(std::find(v.begin(), v.end(), starts_from_zero));
    
  • If you have some criterion and want to remove all elements that fit that criterion, you need the erase-remove idiom again:

    v.erase(std::remove_if(v.begin(), v.end(), starts_from_zero)),
            v.end());
    
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510