Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some warnings and general cleanup #248

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

voidanix
Copy link
Member

  • Initialize x, y, z inside vec
  • Remove volatile keyword from errors counter
  • Clear objects of now trivial type
  • Fix possible uninitialization of height
  • Convert weapon selection system to std::vector
  • Fix -Wnon-pod-varargs error on clang 14
  • Fix -Wint-in-bool-context
  • Document what the sound enums actually are
  • Fix unneeded parenthesis
  • Fix uninitialized values in md3 structs
  • Fix comparison between different signedness
  • Fix address of array always returning true
  • Remove set but unused variables
  • Fix implicit conversion between float and int
  • Fix shift with negative value
  • Attempt to fix misleading indentation warning

Copy link
Member

@TheAssassin TheAssassin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a first look. Mostly great improvements of the code. I have a few remarks I'd like to discuss, though.

src/engine/irc.cpp Show resolved Hide resolved
src/engine/renderva.cpp Show resolved Hide resolved
src/game/ai.cpp Outdated Show resolved Hide resolved
src/game/client.cpp Show resolved Hide resolved
src/game/client.cpp Outdated Show resolved Hide resolved
src/shared/ents.h Outdated Show resolved Hide resolved
src/game/server.cpp Outdated Show resolved Hide resolved
@@ -1055,7 +1055,7 @@ namespace physics
if(curmat == MAT_WATER || oldmat == MAT_WATER)
{
const bvec &watercol = getwatercol((curmat == MAT_WATER ? matid : pl->inmaterial) & MATF_INDEX);
mattrig(bottom, watercol, 0.5f, int(radius), PHYSMILLIS, 0.25f, PART_SPARK, curmat != MAT_WATER ? S_SPLASH2 : S_SPLASH1);
mattrig(bottom, watercol, 0.5f, int(radius), PHYSMILLIS, 0.25f, PART_SPARK, (curmat != MAT_WATER ? S_SPLASH2 : S_SPLASH1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a readability change, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of: the compiler is probably interpreting this as MAT_WATER ? S_SPLASH2 : S_SPLASH1 and then comparing against curmat

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!= is computed before ?:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@robalni is right here. If you want to make this more explicit (and improve readability), you have to put brackets around the != comparison.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That does not work I'm afraid:

bn/src/game/physics.cpp: In function ‘void physics::updatematerial(physent*, const vec&, const vec&, bool)’:
bn/src/game/physics.cpp:1058:115: warning: ‘?:’ using integer constants in boolean context, the expression will always evaluate to ‘true’ [-Wint-in-bool-context]
 1058 |                 mattrig(bottom, watercol, 0.5f, int(radius), PHYSMILLIS, 0.25f, PART_SPARK, (curmat != MAT_WATER) ? S_SPLASH2 : S_SPLASH1);
      |                                                                                                                   ^

Copy link
Contributor

@robalni robalni Jun 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the problem here is that in the definition of the macro mattrig, the last argument mw is used like:

if(mw >= 0)

and this expands to:

if(curmat != MAT_WATER ? S_SPLASH2 : S_SPLASH1 >= 0)

which is the same as:

if( (curmat != MAT_WATER) ? S_SPLASH2 : (S_SPLASH1 >= 0) )

so I think this macro needs to be fixed by adding parentheses around the arguments:

if((mw) >= 0)

and fun fact, when curmat is MAT_WATER then (S_SPLASH1 >= 0) is used and this equals 1 and is the same as S_GUIBACK which means that S_GUIBACK has been played where S_SPLASH1 should have been played.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome catch! Looks a bit ugly now though 👀

Not even worth converting into a function because this macro is used twice in the codebase...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case it might just be removed entirely. That tiny bit of code duplication is acceptable.

src/game/game.h Outdated Show resolved Hide resolved
src/game/game.h Show resolved Hide resolved
src/game/aiman.h Outdated Show resolved Hide resolved
@voidanix
Copy link
Member Author

Thanks for the reviews @TheAssassin and @robalni ! Updated the branch

@voidanix
Copy link
Member Author

voidanix commented Jul 9, 2022

FYI the inrange function now uses constexpr (it will be reused in a later refactor) and the custom NULL macro has been removed

Copy link
Member

@TheAssassin TheAssassin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a few more things.

Please also clean up the branch history when you're done.

game::player1.loadweap.clear();
for (const auto i : items) {
if (std::find(game::player1.loadweap.begin(),
game::player1.loadweap.end(), i) == game::player1.loadweap.end()) // NOT found
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment is not needed, even less experienced C++ programmers should know this idiom.

Also I'm pretty much the opposite of a fan of inline comments. They just increase the line length for no apparent reason and are hard to spot/find/read as well.

src/game/client.cpp Outdated Show resolved Hide resolved
game::player1.loadweap.end(), i) == game::player1.loadweap.end()) // NOT found
{
game::player1.loadweap.emplace_back(i);
if (game::player1.loadweap.size() >= W_LOADOUT)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wrap all conditional code (e.g., branches of an if) in braces to create a proper scope. It is a common issue in refactorings to add lines to existing code without realizing that the scope had ended at the semicolon. This can be avoided by always using a scope.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work?:

if (std::none_of(game::player1.loadweap.begin(),
                game::player1.loadweap.end(), [=](auto d) { return d == i; }))

I hope there isn't too much overhead if done like this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be avoided by always using a scope.

That is silly and adds unnecessary LoC; even the Kernel's coding style agrees:

Do not unnecessarily use braces where a single statement will do.

Copy link
Member

@TheAssassin TheAssassin Aug 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because the Linux kernel uses it doesn't mean it's beginner friendly. Quite the opposite, really. I've seen this kind of problem too often, so I always use a scope now. It's a pre-emptive measure used to prevent branching problems.

"Saving lines of code" is an argument which I often heard before, but it is not a good one. It might have been relevant, back in the days when there were just 12 lines on one screen. Compacting the code too much like this does not aid readability, quite the opposite, and is often used to compact and therefore kind of "hide" complexity. Look at this line, it's probably too long already.

Python for instance, while not everyone likes the lack of braces, does this very well in general. Whenever the code branches, moving the conditional code on a new line (and therefore adding some indentation as well) is very strongly recommended. You could use the same line, but this is ugly and cumbersome. See https://docs.python-guide.org/writing/style/#one-statement-per-line.

Regarding braces, many popular code styles demand them, there are just few exceptions, mostly for old projects who had established such a style long ago. Not using braces "saves" 1-2 lines depending on the code style. I personally prefer to move the opening one on the same line as the conditional/loop statement. As stated in #248 (comment), this ensures that on future refactorings, new code won't accidentally be added in the hope that it will be conditional as well. Furthermore, this also aids the readability of the code. You can quickly see where branching happens. New variables have a clear scope. You can't be surprised when commenting out lines. You do not have to be careful about the semicolon being the end of the conditional code block.

This is not an obfuscated code competition. The code should be easy to read. Branches should be easily visible. Code should be written with the need for future changes in mind.

Edit: kernel code is among the worst code I've ever seen. This is only partly due to code style problems. It's largely C89-style, even though it's been over 30 years since that standard has been released, and there have been multiple new standards since (even C90 introduced so many positive changes, yet kernel code does not use any, e.g., comments). It uses tabs, which the majority of coders rejects for spaces. It's largely undocumented (i.e., the "what the heck were they thinking when writing that" bit is missing). And these are just a few examples...

src/game/game.h Outdated Show resolved Hide resolved
src/game/game.h Outdated Show resolved Hide resolved
src/game/physics.cpp Show resolved Hide resolved
@@ -3395,7 +3395,8 @@ namespace server
}
prev.clear();
}
if(!buf.empty()) setmods(sv_previousmaps, buf.c_str());
if(!buf.empty())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're at it, please add spaces in between statements such as if and for and the corresponding conditions.

We really need clang-format to auto check changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current style is to not have a space there. I'm not a big fan of mixing styles because the point of a style is the consistency that it gives.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current style is to not have a space there. I'm not a big fan of mixing styles because the point of a style is the consistency that it gives.

That is the codebase's old style: we aren't fans of mixing stuff either but every new piece of code should be adopting the new style.

#ifdef NULL
#undef NULL
#endif
#define NULL 0
Copy link
Contributor

@robalni robalni Aug 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason to replace a well defined NULL value with an implementation defined value? As we can read on https://en.cppreference.com/w/cpp/types/NULL the value of NULL can be 0 or nullptr. These two values have different sizes on 64 bit computers and not knowing which one it is can cause problems in some situations, like when using varargs where you specify which type you want to read but NULL makes sure that you don't know which type you give to the function.

I think a better change (if we want to change it) might be to define it as nullptr or (void *)0 or (size_t)0 to make sure that it has the same size as a pointer because NULL is often used where you would use a pointer. Another solution could be to ban NULL (but then we would have to change in a lot of places) by defining it to a value that will generate a compilation error when used. That could be done if we want to avoid confusion that people think they are using the standard library NULL when they are really using our NULL.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, this partly solves the -Wzero-as-null-pointer-constant warning which is (yet?) not enabled in BN.

NULL is often used there you would use a pointer.

Almost always, actually: indeed using nullptr would totally eliminate the issue of weird conversions.

like when using varargs where you specify which type you want to read but NULL makes sure that you don't know which type you give to the function.

I fail to see how different sizes actually makes things act weirdly, but using nullptr (which must be passable through varargs) would be nicer too.

Another solution could be to ban NULL

I encourage everybody to use nullptr, but some files are still using pure C here: once a conversion of sorts is completed, then sure.

FWIW, while my NULL expands to __null, I know that MS's (and MINGW's, according to libcxx) expands to 0: see this commit as a stop gap, because I would much rather see some platforms spit out warnings about NULL than giving an error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fail to see how different sizes actually makes things act weirdly

It's something I read somewhere but I'm not able to reproduce it so I don't know if it's actually a problem.

but some files are still using pure C

If we make a new constant like #define null ((void *)0) that can be used in C too.

my NULL expands to __null

And my NULL expands to ((void *)0) on Debian/amd64/gcc :)

I would much rather see some platforms spit out warnings about NULL than giving an error

Yes, it's probably a bad idea to hard ban NULL because there can be code (like in the standard library itself) that uses NULL that we don't easily control and then that code would break. For the same reason it might actually be a good idea to leave NULL defined as in the standard library because maybe there is some code in there that has expectations on the definition of NULL. I don't know.

voidanix and others added 15 commits May 4, 2023 19:21
Also introduces a new "inrange" function, useful for future refactorings
to std::vector
The mattrig macro expands the mw argument at one point into:

if (curmat != MAT_WATER ? S_SPLASH2 : S_SPLASH1 >= 0)

Co-authored-by: Robert Alm Nilsson <[email protected]>
There should be no reason to overwrite this as the C++ spec already
specifies it. Compilers nowadays also gift us a dedicated warning for this
kind of situation (-Wzero-as-null-pointer-constant).

Remove it and specify <cstddef> to still make NULL accessible.
This usage of volatile has been deprecated since C++20: use std::atomic
in its place.
@voidanix
Copy link
Member Author

voidanix commented May 4, 2023

Ping, any updates on this?

@voidanix voidanix mentioned this pull request May 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants