0

I have a project where I draw a triangle using points, it is called Sierpinski Gasket. To achieve that I am using a class Vx which holds x and y values. The problem is it throws different values if any method is virtual and I wonder why. How virtual functions affect the way the points are generated in the following OpenGL code?

My vector class has the following declaration:

    class Vx {
public:
    float x;
    float y;

    Vx();
    Vx(const float& value);
    Vx(const float& x, const float& y);
    Vx(const Vx& v);

    float getX() const;
    void setX(const float& x);
    float getY() const;
    void setY(const float& y);

    const float operator [] (int i) const;
    operator const float* () const;
    operator float* ();
    float& operator [] (int i);
};

std::ostream& operator<<(std::ostream& os, const Vx v);

Vx operator+(const Vx& p1, const Vx& p2);

Vx operator/(const Vx& p1, const Vx& p2);

And it's implementation is:

Vectors::Vx::Vx() : x(0), y(0)
{
}

Vectors::Vx::Vx(const float& value) : x(value), y(value)
{
}

Vectors::Vx::Vx(const float& x, const float& y) : x(x), y(y)
{
}

Vectors::Vx::Vx(const Vx& v) : x(v.x), y(v.y)
{
}

float Vectors::Vx::getX() const
{
    return this->x;
}

void Vectors::Vx::setX(const float& x)
{
    this->x = x;
}

float Vectors::Vx::getY() const
{
    return this->y;
}

void Vectors::Vx::setY(const float& y)
{
    this->y = y;

}

float& Vectors::Vx::operator[](int i)
{
    return *(&x + i);

}

const float Vectors::Vx::operator[](int i) const
{
    return *(&x + i);
}

Vectors::Vx::operator const float* () const
{
    return static_cast<const float*>(&x);
}

Vectors::Vx::operator float* ()
{
    return static_cast<float*>(&x);

}

std::ostream& Vectors::operator<<(std::ostream& os, const Vx v)
{
    os << "Vx: " << "x = " << v.x << ", y = " << v.y << std::endl;
    return os;
}

Vx Vectors::operator+(const Vx& p1, const Vx& p2)
{
    return Vx(p1.x + p2.x, p1.y + p2.y);
}

Vx Vectors::operator/(const Vx& p1, const Vx& p2)
{
    return Vx(p1.x / p2.x, p1.y / p2.y);
}

My main class starts setting up OpenGL to be ready to draw, and at some point I create a buffer with the data as following:

void sierpinskiGasket() {

using namespace Vectors;

/*
Creamos la informacion para el triangulo
*/
const int numPoints = 5000;
Vx points[numPoints];
points[0] = Vx(0.0f, 0.0f);

Vx vertices[] = {
    Vx(-0.50f, -0.50f),
    Vx(0.50f, -0.50f),
    Vx(0.0f, 0.50f)
};

for (int i = 1; i < numPoints;i++) {
    int j = rand() % 3;

    points[i] = (points[i - 1] + vertices[j]) / 2;
}

/*
    Generamos el buffer para el triangulo
*/
unsigned int triangle_vbo;
glGenBuffers(1, &triangle_vbo);
glBindBuffer(GL_ARRAY_BUFFER, triangle_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
}

The result I get is the next triangle (which is okay):

enter image description here

But if I change any method of my Vx class to virtual, the next thing happens:

enter image description here

  • 3
    `virtual` functions are unlikely to be the problem, given the code you've shown, since you don't appear to be using any indirection (e.g. calling functions to derived classes through a pointer or reference to base). What's more likely to be the problem is that you're treating your `x` and `y` members as if they're part of an array, which they are not, and this will cause Undefined Behaviour. I suggest getting either rid of `operator[]` and `operator float*` completely, or replacing `x` and `y` with an actual array like `float xy[2];` – alter_igel Dec 04 '20 at 17:39
  • Take a look at [Accessing struct data members via pointer arithmetic](https://stackoverflow.com/questions/58428562), which seems almost like a duplicate of this question – alter_igel Dec 04 '20 at 17:46
  • see [_vec2](https://retrocomputing.stackexchange.com/a/6055/6868) on how to merge array like and `.x,.y` access to a class – Spektre Dec 05 '20 at 08:10

2 Answers2

1

Class with virtual methods has implicit field - pointer to VMT (virtual method table). You can check it by printing sizeof(Vx) with and without virtual methods. Internal representation of classes and structs is not specified by the C++ standards and varies between compilers, so you should not pass classes with virtual methods to functions like glBufferData.

Doj
  • 1,244
  • 6
  • 13
  • 19
1

After some research, I found a more detailed answer to this question since it explains why virtual functions affect the way class members have different memory allocations throwing different results, in addition with some documentation from the c++ reference that explains about polymorphic objects and memory allocation too.

In fact, it is undefined behaviour and the answer to the following question explains why in a short and better way.

https://stackoverflow.com/a/48300540/13716517

But also I would like to add what the c++ reference says from the next 2 links:

https://en.cppreference.com/w/cpp/language/object

https://en.cppreference.com/w/cpp/language/memory_model

Where we can find the next documentation from:

Polymorphic objects: Objects of a class type that declares or inherits at least one virtual function are polymorphic objects. Within each polymorphic object, the implementation stores additional information (in every existing implementation, it is one pointer unless optimized out), which is used by virtual function calls and by the RTTI features (dynamic_cast and typeid) to determine, at run time, the type with which the object was created, regardless of the expression it is used in. For non-polymorphic objects, the interpretation of the value is determined from the expression in which the object is used, and is decided at compile time.

Memory location: A memory location is an object of scalar type (arithmetic type, pointer type, enumeration type, or std::nullptr_t) or the largest contiguous sequence of bit fields of non-zero length Note: Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation.

Taking all that in consideration, we can see where the undefined behaviour for using virtual functions comes from in more detail, understanding how they change the behaviour when generating the points.

  • Your code has Undefined Behaviour regardless of your use of virtual functions because you are performing pointer arithmetic on member variables, which causes Undefined Behaviour. Different behaviour due to other changes like adding virtual functions is just one of the myriad possible outcomes when you have UB. Please re-read my comments on your question. – alter_igel Dec 06 '20 at 20:33
  • I understand what you said and thank you in advance for taking the time for answering, but what I was asking how virtual functions cause that undefined behaviour and you didn't explain why. After some research I finally found why, since adding virtual functions to my class add some extra memory allocations which cause my Vx class member variables memory allocations don't be in a row as without virtual functions. Even if I delete the virtual keyword from my methods it would still being undefined behaviour due to other reasons, but I was wondering the virtual part. – Josael Perez Dec 06 '20 at 21:06