Sign in to follow this  
Cygon

Cast vector(Derived *) to vector(Base *)

Recommended Posts

Actually, my problem is a bit more complicated than the subject gives away. I want to cast a vector< shared_ptr<Derived> > to a vector< shared_ptr<Base> >.
struct InternalManager {
  vector< shared_ptr<XyzConnection> > ActiveConnections;
};

class PublicApi {
  typedef vector< shared_ptr<Connection> > ConnectionVector;
  const ConnectionVector &GetActiveConnections() const {
    return InternalManager::ActiveConnections;
  }
};
I know that technically, I could just reinterpret_cast<> the whole thing, but of course, that's a horrific way to solve my problem. Another solution would be to write my own collection interface:
struct ConnectionList {
  public: virtual size_t size() const = 0;
  public: virtual shared_ptr<Connection> Get(size_t index) = 0;
};
struct XyzConnectionList : public ConnectionList {
  public: virtual size_t size() const { return this->connections->size(); }
  // Darn! Cannot use covariant returns due to shared_ptr<> here!
  public: virtual shared_ptr<Connection> Get(size_t index) {
    return this->connections.at(index);
  }
  public: virtual const shared_ptr<XyzConnection> &GetImpl(size_t index) {
    return this->connections.at(index);
  }
  private: vector< shared_ptr<XyzConnection> > connections;
};
But maybe someone here knows a shorter way that doesn't require me to reimplement a full collection interface with iterators and stuff just to avoid copying a possibly large vector? -Markus-

Share this post


Link to post
Share on other sites
You can't do it, because it's not moral. Casting a vector<Derived*> to a vector<Base*> would imply that a vector<Derived*> IS-A vector<Base*>. But that's not true, because there's something vector<Base*> can do that vector<Derived*> can't: Insert Base*s that aren't Derived*s. Therefore, such a cast could lead to illegal operations.

Now, that's sort of a weak excuse. A const vector<Derived*> is indeed a const vector<Base*>, but you can't do that either. The best you can do is make your function take an iterator pair instead of a container, and template it on the iterator type. Not perfect, but neither's C++.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Now, that's sort of a weak excuse. A const vector<Derived*> is indeed a const vector<Base*>, but you can't do that either.


Actually it isn't. For a given Derived object the bit pattern for the Derived * and the Base * for the object aren't necessarily equal.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Quote:
Original post by Sneftel
Now, that's sort of a weak excuse. A const vector<Derived*> is indeed a const vector<Base*>, but you can't do that either.


Actually it isn't. For a given Derived object the bit pattern for the Derived * and the Base * for the object aren't necessarily equal.

Yes, I meant IS-A in the parametric polymorphic sense. That is, every operation which is defined on const vector<Base*> is defined on const vector<Derived*> (taking covariance into account, and extending the polymorphism to the const_iterators).

Share this post


Link to post
Share on other sites
Not exactly. The situation with float and double is a little more complicated, because there's no type ordering between them. An operation expecting a double cannot take a float, nor vice versa, without the interposition of a conversion operation which comes up with a new value of the appropriate type. (Admittedly, the fact that this conversion is implicit--something anathema to people who use phrases like "parametric polymorphism"--makes it a difficult area to talk about without ambiguity.)

Share this post


Link to post
Share on other sites
The situation with const vector<Base*> and const vector<Derived*> is the same, because there's no type ordering between them. An operation expecting a const vector<Base*> cannot take a const vector<Derived*>, nor vice versa, without the interposition of a conversion operation which comes up with a new value of the appropriate type.

Share this post


Link to post
Share on other sites
Quote:
Original post by Cygon
Actually, my problem is a bit more complicated than the subject gives away. I want to cast a vector< shared_ptr<Derived> > to a vector< shared_ptr<Base> >.


You can't, and furthermore, you really don't want to. Returning the vector of connections is probably exposing too much anyway. Try offering an iterator interface instead, or just a member function to get the Nth active connection. In the second case, you should be able to convert to a boost::weak_ptr<Connection> fairly easily to hide the information, and in the first it should still be doable (though you'll be entering the somewhat scary world of iterator adaptation). You don't need a full collection interface - just the parts that outside code are interested in. Why should it care that your active connections are stored in a vector, anyway?

Share this post


Link to post
Share on other sites
Quote:
I want to cast a vector< shared_ptr<Derived> > to a vector< shared_ptr<Base> >.
Whilst some people responding may start down the path of figuring out contrived ways of doing such a thing, I'm going to state that you need to take a step back and explain what let you to the situation where you would want to do this.
Chances are that situation can be re-examinined with the conclusion that there is a better way of doing things. Please provide information about the overall goal.

Share this post


Link to post
Share on other sites
Quote:
The situation with const vector<Base*> and const vector<Derived*> is the same, because there's no type ordering between them. An operation expecting a const vector<Base*> cannot take a const vector<Derived*>, nor vice versa, without the interposition of a conversion operation which comes up with a new value of the appropriate type.

Ah, but the nature of the conversion is different. For float/double, the conversion changes the data fundamentally (for a clearer picture, think of float/int). In contrast, the conversion here would be purely representational, twiddling bits to keep the machine happy. Put differently, in a perfect world, there would be two valid ways to type-check this function (though, of course, C++ chooses a particular one), which would nevertheless produce identical results:

template<typename T>
Base* getThird(vector<T> const& v)
{
return v[3];
}

vector<Derived*> v;
getThird(v);

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Ah, but the nature of the conversion is different. For float/double, the conversion changes the data fundamentally (for a clearer picture, think of float/int). In contrast, the conversion here would be purely representational, twiddling bits to keep the machine happy.

For that argument to hold you have to assume that Derived * and Base * have the same bit pattern representation, which is not a valid assumption. In order for a const vector<Derived *> to be passed to a function that accepts a const vector<Base *>, you need to go down the line and convert each and every stored Derived * to a Base * before the body of the function is invoked. This can't be done inline since the objects are const, so in other words creating a temporary just like a float to double conversion that happens when you try to invoke a float function with a double or vice versa.

Share this post


Link to post
Share on other sites
I'm not saying that it's pragmatically possible in C++. I'm just saying that other than the underlying data representation, there's no good excuse not to be able to extend the type system with that conversion, given that the outward interface of the two types are compatible.

Share this post


Link to post
Share on other sites
You are able to extend C++'s type system with that relationship, with an explicit creation of a temporary. Or if you feel like it:

template <typename T, typename U>
const vector<T> const_vector_cast(const vector<U> & value) {
return vector<T>(value.begin(), value.end());
}

But again, since this involves the creation of a temporary, it doesn't seem any more of an IS-A relationship than float-double.

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
Whilst some people responding may start down the path of figuring out contrived ways of doing such a thing, I'm going to state that you need to take a step back and explain what let you to the situation where you would want to do this.
Chances are that situation can be re-examinined with the conclusion that there is a better way of doing things. Please provide information about the overall goal.


I just wanted to know whether there's a safe and elegant way to achieve such a cast that I'm missing, not to perform it at any cost. If there isn't, I can simply write a proxy for the vector, create my own collection interface, return a copied vector<Base *> or redesign my interface of course.

I'm not some bloody amateur who'd go ahead and rape C++ if my design doesn't work out :)

-Markus-

Share this post


Link to post
Share on other sites
Quote:
Original post by Cygon
Quote:
Original post by iMalc
Whilst some people responding may start down the path of figuring out contrived ways of doing such a thing, I'm going to state that you need to take a step back and explain what let you to the situation where you would want to do this.
Chances are that situation can be re-examinined with the conclusion that there is a better way of doing things. Please provide information about the overall goal.


I just wanted to know whether there's a safe and elegant way to achieve such a cast that I'm missing, not to perform it at any cost. If there isn't, I can simply write a proxy for the vector, create my own collection interface, return a copied vector<Base *> or redesign my interface of course.


/me points back excitedly at his previous post. Just give back iterators (you'll probably need/want to do some adaptation so that you create an iterator over the pointed-at Connection objects), or individual elements on demand.

... Or then again, you could check again if you really need the flexibility that a vector of shared_ptr gives you - i.e. the flexibility to share objects between containers. If you only want to store objects while respecting polymorphism, and the container "owns" its elements, then a boost::ptr_vector of Connection would be more appropriate. (Although I would still change the interface, even though this would let you return the vector and work with it polymorphically.)

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
I'm not saying that it's pragmatically possible in C++. I'm just saying that other than the underlying data representation, there's no good excuse not to be able to extend the type system with that conversion, given that the outward interface of the two types are compatible.


I don't know, you're basically introducing the concept of const semi-inheritance to an already complex system just to be able to convert between two classes which are effectively leaf nodes of a inheritance tree. Aside from type, they also specify concretely the allocator to be used.

A much simpler solution would seem to be to go up the inheritance tree.

While we're unable to inject parent classes into the C++ standard library hierarchy, we are entirely able to do the next best thing and conjure up a secondary reference/view class which can handle this (admittedly nonexistant) parent, and we have ample opportunity to handle upcasting, differing allocation methods, etc. there.

Call it const_vector_ref, to take a page from Boost.MultiArray's naming conventions. Of course, this gets us back to the apparent needless complexity point, and one wonders if the solution couldn't be to just template the function based on the container or iterators.

If that wasn't an option, my next instinct would be to just put together a pair of industry::virtual_random_access_iterators to industry::static_cast_iterators, to the original container's iterators, since I've already written this code.

Alternatives to static_cast_iterator would include some as of yet unwritten implicit_cast_iterator, or hacking implicit casting into my virtual iterators.

Since we're already presupposing const access (to avoid the original moral objection raised of DerivedFromBase2* in a DerivedFromBase1* container), that should provide all the necessary access anyways (unless you really really need .capacity(), end-begin for the equivilant of .size() not being enough for your greedy arse). Of course, the virtual access for every single operation may make copying actually superior in performance, but...

(in case you're wondering where the iterator source is)

EDIT:

To get back closer to where the OP is probably thinking about, wanting to get connections by index rubs me just about every wrong way possible. Connections come and go, so I'm left wondering why you'd use this presumably unstable key in the first place. Why you have separate collections of XyzConnection that need to be handled elsewhere as collections of Connection also leaves me wondering. Combined with the example identifiers which hopefully don't even exist (XyzConnection), the whole shebang feels horribly over engineered, over thought in the wrong directions, and Java-like. Enterprisey. Hopefully that's just the opaqueness, but it's hard to point out the best direction to go when the original premise feels faulty in the first place. Not enough data.

[Edited by - MaulingMonkey on March 7, 2008 3:52:48 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this