0

I've written a series of function templates to convert arbitrary stuff to text as painlessly as possible. For example, print(std::pair<int, int> {13, 1}); will print {13, 1} and something longer like

std::vector<std::tuple<double, std::string>> vect;
for(int i=0;i<3;++i) {
    double root = sqrt(i);
    vect.push_back( {root, "sqrt " + std::to_string(i) } );
}
print(vect);

Will output: { {0, "sqrt 0" }, {1, "sqrt 1"}, {1.41421, "sqrt 2"} }

Let's say I have the following struct:

struct point { int x, y; };

How dangerous is it to write something like the following code?

std::vector<point> my_points;
//Add points into my_points;
print(reinterpret_cast<const std::vector<std::pair<int, int>>&>(my_points));

It compiles in gcc and it produces the expected output, although I'm concerned it could fail if someone were to try porting the code.

Alecto Irene Perez
  • 10,321
  • 23
  • 46
  • std::vector and std::vector> is different classes, so it's reinterpret_cast have to be used. And behaviour is implementation dependent – user5821508 Jul 27 '17 at 15:08

3 Answers3

3

First of all, you are not doing any dynamic casting here, since there is no a single call to dynamic_cast in your code. What you are doing here is the reinterpet_cast, produced by the c-cast.

Second of all, no, it is not safe and is undefined behavior on many levels.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
1

Very dangerous. You have undefined behavior. std::vector<std::pair<int, int>> and std::vector<Point> are completely, district and separated classes that have no relation to each other. Reinterpreting one to another will be undefined behavior.

In fact, there are cases defined by the standard that will happen to not work to. Try casting a std::vector<char> to std::vector<bool>. Even though char and bool have the same size, both vector are not compatible.

If you want to avoid copying the buffer, consider using templates:

template<typename T>
void print(const std::vector<T>& vec) {
    // ...
}

Even better, don't force vector. If you have a std::array<Point, n>, you might want your function to work:

template<typename T>
void print(const T& range) {
    // ...
}
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • I am using templates - that's why it's able to print so many different things to begin with. The print function can print any vector, tuple, map, set, or pair, as long as the elements of the vector/tuple/map/set/pair can be printed by the print function, and aside from that it can print anything that's convertible to a string. I was just wondering how dangerous it was to do what I did above instead of writing out a print function. – Alecto Irene Perez Jul 27 '17 at 15:20
0

Instead of passing a std::vector<T>, you could pass two const T*, one const T* and a length, or a struct of two pointers.

Now you know that there is no specialization (as with the case std::vector<bool>, see Why is vector<bool> not a STL container?) going on, and you also know that you have contiguous memory of T:s. Now provided that you know that

  1. Foos and Bars have exactly the same layout (alignment, size)
  2. Foos and Bars share used behavior (If there is a function f(Foo), and that you use that function in print, there must also be a semantically identical function f(Bar)). A more theoretical formulation would be something like: Foo is equivalent to Bar with respect to f.

Then, it should be safe, but you are on your own, because the compiler cannot really check (2). As an example, take two version of Point (lets use float instead of int):

struct PointXY{float x;float y;};

struct PointYX{float y;float x;};

float inner_product(PointXY p1,PointXY p2)
    {return p1.x*p2.x + p1.y*p2.y;}

float inner_product_with_important_y(PointXY p1,PointXY p2)
    {return 0.5f*( p1.x*p2.x + 4.0f*p1.y*p2.y );}

That is, you can safely call inner_product with any combination PointXY and PointYX (both (1) and (2) are fulfilled), but as soon as you call the anisotropic version, you will get wrong results ((1) is fulfilled, but not (2)).

user877329
  • 6,717
  • 8
  • 46
  • 88