The Partridge Family were neither partridges nor a family. Discuss.
feli_lecchini
Posts: 15
Joined: November 7th, 2022, 12:58 am

I have a simple structure to represent position and velocity defined as:

struct Vector2Float
{
constexpr Vector2Float& operator += (const Vector2Float& rhs) const
{
return Vector2Float
{
this->x + rhs.x,
this->y + rhs.y,
};
}
constexpr Vector2Float& operator * (const float rhs) const
{
return Vector2Float
{
this->x * rhs,
this->y * rhs
};
}

float x;
float y;
};

My entire solution compiles but when I run the game the operator += doesn't do the job. I'm coding the Fartannoyed game and the ball doesn't move when the code executes "ball.position += velocity * dt" where dt is a float representing the time between frames. The funny this is when I change the code of the operator += like this:

constexpr Vector2Float& operator += (const Vector2Float& rhs)
{
this->x += rhs.x;
this->y += rhs.y;

return *this;
}

the ball moves. I don't understand where is my error in my first definion of += ????????

John73 John
Posts: 14
Joined: August 6th, 2020, 7:16 pm

The problem is the lines:

Code: Select all

return Vector2Float
{
this->x + rhs.x,
this->y + rhs.y,
};
It looks like you're trying to construct a new Vector2Float and then return it, but the += operator is supposed to work by modifying its original lefthand operator directly, not creating a new one.

For example, "a += 7" increases the value of "a" by 7. It doesn't create a new variable equal to "a + 7" if that makes sense. So when you change it to:

Code: Select all

this->x += rhs.x;
this->y += rhs.y;

return *this;

This is the correct way of doing it, as this will modify the x and y of the original "this" and then return it.

BTY I'm kind of curious why both of these functions are "constexpr"? It's been a long time since I did that project but I don't remember Chili specifying that they should be defined that way.

feli_lecchini
Posts: 15
Joined: November 7th, 2022, 12:58 am

Thanks for the answer, it all makes sense now. About the constexpr thing, before I used to put nothing but my compiler complained about the "operator overloading" being redefined in many other files of my project. I fix those errors by placeing "inline" before the return type of the += operator. But then by doing a little experimentation i also discovered that by placing "constexpr" the errors were gone too. Since then I prefered to place "constexpr" before the return type. I know this doesn't explained anything (i'm just a c++ newbie) but maybe it can help you research in the right direction.

John73 John
Posts: 14
Joined: August 6th, 2020, 7:16 pm

It sounds like you're not declaring the operators correctly and you have a namespace issue maybe?

Here's the proper way to do it.

The declaration, in Vector2Float.h:

Code: Select all

// In the "Vector2Float" namespace
struct Vector2Float
{
// return type is a Vector2Float reference
Vector2Float& operator+=(const Vector2Float& rhs);
// Also all the other functions and data members
}
and the definition in Vector2Float.cpp:

Code: Select all

#include "Vector2Float.h"

Vector2Float& Vector2Float::operator+=(const Vector2Float& rhs)
{
// stuff
}
The "Vector2Float::" right before "operator+=" indicates that this operator is in the "Vector2Float" namespace. Namespaces are ways of keeping code in categories. So if some other class has a += operator overloaded, the compiler won't get confused. It knows this one belongs to Vector2Float while the other one belongs to the other operator. I think that might be why it was yelling at you.

feli_lecchini
Posts: 15
Joined: November 7th, 2022, 12:58 am

1) Added Vectors.h file in my solution.
2) In that file I defined a struct name "Vector2Float" with its members and functions.
3) Then I needed operators "vector x scalar" and "scalar x vector".
4) The first one you can define it inside the struct because it is an lvalue.
5) The last one you have to define it outside the struct (because you can't overload the operator "*" for a primitive like float), so i overloadad the operator this way: Vector2Float& operator *= (const float lhs, const Vector2Float& rhs) {return Vector2Float{ lhs * rhs.x, lhs * rhs.y }; }.

6) I proceed to include the Vectors.h file in other header files where I need my struct.
7) I try to compile and I get the error "redefinition of opertator "*" blah blah blah...."
8) I proceed to place "inline" or "constexpr" before the return type of the binary operator and the whole solution compiles and runs.
9) So, from now on, every time i define a binary operator overloading I use the form: "constexpr typeA& operator @(const typeA& lhs, const typeB& rhs) {...}

John73 John
Posts: 14
Joined: August 6th, 2020, 7:16 pm

Ah, I think I see where you went wrong. You can declare a function in the .h file, but you should define it in the associated .cpp file. The declaration is a single line like this:

Code: Select all

Vector2Float& operator *= (const float lhs, const Vector2Float& rhs);
That's the only part of this function that should be in the header file. It just tells the compiler some basic information about the function:
• return type (Vector2Float reference)
• name you use when calling it (in this case it's the *= operator but it could be "GetPosition" or "ShootTarget" or anything you want)
• the parameters the function takes, which is the stuff between the parentheses

The function's definition is the body of the function that actually does stuff. It usually shouldn't be in the .h file, it should be in the .cpp file. It's going to work the same as your struct's member functions, just without the "Vector2Float::" in front.

The reason is that the .h file is probably going to be #included in multiple other files. So if you include it in FileA.cpp and also in FileB.cpp, the compiler will see two functions with the same name and get confused. If it's just the declaration, that's fine but if it's the definition (body of the function where the stuff happens) then you get that redefinition error.

Making it a constexpr is a workaround for certain special circumstances, but you should get in the habit of only defining functions in the .cpp file. Otherwise it will lead to more and more problems down the road.

feli_lecchini
Posts: 15
Joined: November 7th, 2022, 12:58 am

I see...but what about the #pragma once directive ? I though it was there to prevent re-definitions !

John73 John
Posts: 14
Joined: August 6th, 2020, 7:16 pm

#pragma once is an instruction to the pre-processor that prevents a different kind of redefinition. If FileA.h is #included in FileB.h, and then both A and B are #included in FileC.cpp, then without #pragma once, there would be 2 copies of A included in C. That's what #pragma once protects against. The compiler sees that A has already been included in C once, so it skips it the second time.

However the redefinition problem you had was different. If A.h is included in B.cpp and C.cpp (but B isn't included in C, since you don't #include .cpp files) then each file is only getting one copy of A, which is what you want. So #pragma once doesn't need to step in. Everything is fine, unless A has a function body in it. When you get to the next stage of the process, the compiler will see that a single function has two bodies. It doesn't know which one to call, so it gives you the error.

I hope that makes sense. It's kind of hard to explain.

feli_lecchini
Posts: 15
Joined: November 7th, 2022, 12:58 am