🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

What practically annoys you on a regular basis in programming languages?

Started by
70 comments, last by Dawoodoz 4 years, 8 months ago
15 hours ago, Kylotan said:

That in itself is one of the things I hate about C# - the idea that the type of the object dictates where it gets stored. I often need an object to be a class type, e.g. because I need inheritance, but I may also want to store a cheap temporary, or to have a ton of them allocated quickly and close together in a memory-efficient data structure.

I hate micro-optimisations but as a game developer I need the language to help me with the more trivial optimisation decisions rather than forcing me through gymnastics to avoid the memory allocator.

Not sure about the C# GC, but for comparison, the Dart GC is optimized specifically for working with many short-lived objects (they also dropped the 'new' keyword). It has a multi-tiered (generational) GC approach, using a round-robin stack for all new objects. Only objects that outlive their welcome in that stack get allocated into heap memory. Unfortunately Dart doesn't have proper integer and floating point primitives (it has a JS-like number type), so it's not really an entirely appropriate language for game development.

Fully agree that a language should empower the programmer with choice, rather than enforce default behaviour.

There's a `stackalloc` operator at least for some cases in C#...

Advertisement

From a quick reading through the MSDN, it seems as this is again some rare used compiler magic for a handfull of edge cases the developers of .Net struggled over in their work, stackalloc dosen't seem to be something usefull at the moment.

What I forgot about in my list is

9. Easy Data Access: Objects are memory interpreted in some kind of pattern (the class) or data (primitives) and it is easy to make a byte-Array to an integer and vice versa as long as you know what you are doing and don't access outside of the bounds of your memory. I like in some way the whole concept of pointers to be just memory addresses stored anywhere, they can be shifted, they can freely be converted. In C# the strict type checking is blessing and curse at the same time. The runtime tells you if something goes wrong in type conversion but at the same time you aren't able to assign a byte array to a string easily, even if the length is the same. You can't do math operations or comparsions of huge arrays efficiently because it has to be tested as the type it is and if you have reference types, you have to call the equals operator on every object. In C++ performing a memcmp dosen't care for types of the data or the array itself, it just compares memory regions. If two objects are similar, no problem because it isn't the reference rather than the data that is tested for equality (if it is inplace and not a pointer). I like this kind of fredom in data because it makes life easier sometimes

On 8/12/2019 at 6:51 PM, Alberth said:

Some people completely reject OO, as it is too complicated, and stick with C style coding. While I disagree with their solution, they do have a point, OO makes it extremely simple to merge different but related pieces of code into the same object, or setup references to other objects etc.

On 8/12/2019 at 9:12 PM, ThorMalleuson said:

Everything must be Object Oriented / everything is an object.  Stop trying to model the real world in your code.  All software does is transform data from one form to another, the CPU doesn't care about clever static structures. y = f(x): your code is the 'f' part.  Make those transformations as tight and performant as possible.

In game development, code is indeed generally about transforming one chunk of data into another chunk of data, yeah. The use of OO does not bring that much to the table for that purpose. I think a lot of the OO push comes from the enterprise sector. From that POV, programming is largely concerned with storing and retrieving records of data, where real world associations do make sense.

One of the things I find odd with OO implementations in current languages, is that while the following functions are pretty much equivalent from a technical point of view (aside from calling convention), they are treated as two completely different things.


foo(bar);
bar->foo();

However, at which point should a function be a method in a class, and when should it not? Does it actually even make sense to specifically declare class methods? I'd find it more convenient and style-flexible if these ways of calling a function were just syntactic sugar for the same function. It would also add some sanity to the weird hack that is called "extension methods", essentially make it obsolete.

You could even go as far as making "properties" just syntactic sugar for a function call. A getter is a function that returns something, a setter is just a function that takes some parameters (and yes, why not have properties that take and return tuple values).

As an example, an `int` property named `foo`. Make all three ways of using it valid, and you can code in whatever style you like.


// This is not C

// function int foo(int* this);
a = x.foo(); // Method call
a = x.foo; // Property getter
a = foo(&x); // Function call

// function foo(int* this, int value);
x.foo(a); // Method call
x.foo = a; // Property setter
foo(&x, a); // Function call

 

On 8/12/2019 at 3:51 AM, Alberth said:

I have a class with 3 fields. Why do I have to write a class definition, default constructor, constructor taking the 3 fields, destructor (remembering virtual if it's a base class), copy constructor or move constructor? If I want equality on that class, I need to spell out when an object is equal. If I want ordered, I need to tell exactly how to compute order.

Since you mentioned copy/move constructors I'm assuming this is specifically in C++. IMO a lot of your wants can be addressed by changing your idiom a little. On most classes in a modern idiom you don't need all of those things unless you're specifically writing your own containers. Furthermore I'd argue that some of your complaints aren't actually all that bad. Some points:

  • the standard seems like it adds more and more cases where inline initializers are supported every iteration; we can now assign defaults to fields for a lot (but not all) types
  • separating the default constructor from the class definition is actually a good thing in C++ due to the silliness that is #includes. The separation lets you change the defaults without having to recompile the rest of the codebase
  • you don't strictly need a constructor that just takes the 3 fields if you can use aggregate initialization
  • you only need an explicit copy/move constructor if you're doing something different from what the default behavior is (ie. the class is an "owning" type and the ownership isn't handled by one of the fields); see the "rule of zero"
  • same goes for destructors, the base class scenario aside (but you should be using inheritance sparingly)
  • I agree on equality, that should be automatically generated and I've never been clear on why it isn't
  • I'm not clear on what you propose to do differently for the ordering case, but you also don't need to have that be a feature of the class itself if you're using sorting algorithms that take sorting predicates (because then you can specify the ordering in those).

Personally, my biggest gripe with C++ is everything to do with #includes (which leads to people working around the problem in mutually incompatible ways; it's no wonder single-file and header-only libraries have taken off!) and especially the fact that the standard headers don't follow an "include what you use" philosophy, but C++'20 is theoretically fixing that with modules. Second biggest is probably how the standard doesn't really have a good hash table (unordered_map has some issues that affect perf).

Biggest gripe with C# and Java is that they don't seem to understand that free functions can actually *promote* encapsulation in a real-world codebase and therefore disallow them. Java's generics erasing the type of the thing stored in them and requirement that primitive types be boxed (though at least the language does this automatically) to be stored in generic containers or passed to generic methods is a bit irritating. Java's lack of reference arguments (which makes writing a sort method that works on stack types impossible). Java's lack of stack types (and C#'s distinction at the type level between stack and reference types).

Any expression of the form "Type x = new Type()." I shouldn't have to specify the type more than once in the statement. This is one reason I'm grateful the auto keyword exists and remedying this is the most common use of auto I see in C++.

48 minutes ago, Oberon_Command said:

Biggest gripe with C# and Java is that they don't seem to understand that free functions can actually *promote* encapsulation in a real-world codebase and therefore disallow them.

This.... so much.

At my last role, I inherited a rule that said "no static methods in c#". I scrapped that rule first day (static data is the devil, but static methods are fine). 

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
5 hours ago, Kaetemi said:

In game development, code is indeed generally about transforming one chunk of data into another chunk of data, yeah. The use of OO does not bring that much to the table for that purpose. I think a lot of the OO push comes from the enterprise sector. From that POV, programming is largely concerned with storing and retrieving records of data, where real world associations do make sense.

In my view, OO is about "smart data", data that you can ask questions. "please list, tell me how long you are", "please monthly payment page, give me a summary of your information". Small functions you can "hide", attached to some data. I think this makes perfect sense.

 

The next step is to write some functionality that eg all instances in an inheritance tree share, say, an equality member function. This recurses down the composition tree, until you hit the basic case, and then it recurses back up giving you the result. For me, a more common functionality is to transform an instance to some other form, eg convert an expression instance tree to text.

If you write that the OO way in a class hierarchy with 20 classes (expression classes tend to get a lot of distinct cases), it works, but your single transformation function gets scattered across those 20 classes. Writing it is quite doable, bug hunting is a lot less doable, since to read what the code is doing you need to have 20 tabs in your editor or 20 printouts of a method of 5-10 lines. So instead, I write a single function "transformToText(expr)" (or an "equals(obj1, obj2)" function). That function is a long list of "instanceof" tests, one for each class, and for each case, I write the 5-10 lines of functionality for that class. I find that works much better, even though it totally is against OO methodology. I don't buy the Java/C# gospel (everything is an object), nor do I buy the C gospel (everything is a function), I mix them to write code that is as readable and maintainable as possible.

In my view, OO methodology should say more often when it should not be applied, instead of saying "everything is an object" and then failing its own gospel by having static methods. That would make it more clear which part of coding is still unsolved.

 

6 hours ago, Kaetemi said:

However, at which point should a function be a method in a class, and when should it not? Does it actually even make sense to specifically declare class methods? I'd find it more convenient and style-flexible if these ways of calling a function were just syntactic sugar for the same function.

Python does this, "a.f()" is truly equal to "f(a)", with the technicality that "f" may be findable only by writing "MyClass.f" in the second case. This is why it has "self" as standard argument in all methods of a class.

 

6 hours ago, Kaetemi said:

You could even go as far as making "properties" just syntactic sugar for a function call. A getter is a function that returns something, a setter is just a function that takes some parameters (and yes, why not have properties that take and return tuple values).

Python has a standard @property decorator for this.

Personally, I avoid getters and setters if they don't do anything, I just give public access to the variable instead, with the implied restriction that "looking in data is fine, changing it only if its owner agrees". The underlying idea is that all objects aim for the same goal, and thus cooperate with each other for that goal. No need to protect yourself from your friends.

 

3 hours ago, Oberon_Command said:

I agree on equality, that should be automatically generated and I've never been clear on why it isn't

I like "bool operator==(const A &a1, const A &a2)" a lot more than "a1.equals(a2)". Equality for me isn't instance specific so the latter doesn't make sense at all to me. It could be class specific, but really it's weird if you have more than one equality notion on a class in a single program (as such, specifying that with each use of a sort algorithm may no be the most useful solution).

One of the problems in code is perhaps that we use identity as equality notion a lot, that is, mathematical equality is relatively rare. Perhaps a language should be created that by default has no equality at all.

The difference between equality and order is that the latter requires a specification which fields to compare first (and how, ascending or descending). For this reason I see these cases as being different.

 

6 hours ago, Oberon_Command said:
  • I agree on equality, that should be automatically generated and I've never been clear on why it isn't
  •  I'm not clear on what you propose to do differently for the ordering case, but you also don't need to have that be a feature of the class itself if you're using sorting algorithms that take sorting predicates (because then you can specify the ordering in those).

With C++20 we're also getting defaultable equality and ordering operators along with operator <=>, so in most cases you'll have to write 0 or 1 comparsion operators yourself, instead of the whole set. It's already available in the latest version of MSVC and hopefully GCC and Clang won't be too far behind.

My long biased rant

Automatic nationalization (.NET et al) because it assumes that float to string conversion is only meant to be displayed to users while the majority of strings are stored in files, sent to other countries and then parsed with a different decimal notation. .NET even needs static analysis to warn about using this default feature. Ubuntu's task manager also had a bug where a comma separated vector of french decimals were displayed (1,2, 3, 4,5...) and only disambiguated by white-space.

Using heap allocated objects when there's no reason. Scripting languages and .NET are probably worst regarding this.

Non-deterministic behaviour hides bugs on the computer being developed on. C++, GLSL, ESSL, JavaScript and ShellScript suffers a lot from this, so that a single computer cannot be used to test an algorithm's correctness.

Boiler-plate code is the smell of bad language design, like PIMPL in C++ and Visitor in Java.

C++ iterators are a pointless middle ground that's both slow and unsafe at the same time. Useless for reference implementations by exposing undefined behaviour in case of bugs and deviating from math syntax. Useless for optimization by making it harder to move in strides for SIMD assembly intrinsics and manually schedule memory access based on hardware specifications.

Missing static type-safety is the biggest reason for why I don't use Python. The more you detect before release, the less surprises the user will get.

Explicit white-space is a waste of time. Let source files be plain code without any style. Then let each editor display the code based on local preferences. No more religious style guides making coders feel that managers don't trust them.

On 8/12/2019 at 4:18 PM, Shaarigan said:

The overuse of new: In many cobe bases you always have the new keyword, anything gets new-ed especially in C# but also in C++. You don't want to new everything rather than put it onto the heap or stack for reasons. I'm missing this feature in C# so it isn't possible to just put a class temporary onto the stack.

I tried solving this in a prototype compiler, but it requires a lot of compile-time restrictions and run-time errors to manually place safe reference types on the stack which ended up being highly limited like VB6. Stack and collection memory may not survive the pointers, which is probably why Java does the heap to stack optimization automatically.

In my second experiment, I introduced a complex system of Cyclic, Counted, Dynamic and Fixed types to merge the flexibility of reference types with the purity of value types, but then it became too restricted when each meta type had strict rules about which member types it could contain or inherit from. All generic types would need to be reference types just in case or have multiple versions of bases. The only way to assign meta types (value/reference) automatically would be a cyclic optimization with obscure global error messages.

One can create a C/C++ code generator for plain types to be allocated and freed to experiment and see if a new memory strategy could get the best of safety and speed. No need to make a parser, just lists of data members in each type generating structures with garbage collection methods.

5 hours ago, Dawoodoz said:

No more religious style guides making coders feel that managers don't trust them.

It is unclear to me what would justify that feeling. The purpose of a style guide is to provide canonical answers to common questions regarding how to write the code to keep the programmers from arguing with each other and cut down on the cognitive burden incurred by interacting with the code via well-known conventions for common situations. If management (rather than tech leadership) is imposing the style guidelines, then they're doing style guides wrong, because the style guide should be driven by the developers, for the developers.

This topic is closed to new replies.

Advertisement