🎉 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
25 minutes ago, Oberon_Command said:

What's unintuitive or odd about this? It's certainly not always convenient, but the idea that comparison operators are binary (and more complex conditions are created by composing them) is a simple and clear one.

What's non-intuitive.. When comparison operators are interpreted from this `(5 < b < 10)` into `((5 < b) < 10)`, which I would argue is useless (comparing a boolean with 10), rather than doing a chained expansion into the more useful `(5 < b && b < 10)`.

EDIT: Just found this recent proposal: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0893r0.html

25 minutes ago, Oberon_Command said:

It seems intuitive enough if you keep in mind that char* as "pointer to string" rather than "string value." I would further argue that this is the correct way to think of a char* and "a == b" in your example is the behavior that I would expect. :)

I can agree with that. How about, assuming a strongly typed typedef mechanism is available, and if I'd typedef `char*` as `cstring`. As the pointer is now "hidden" from the programmer, would it be more acceptable to overload `==` on that `cstring` type?

Advertisement
7 hours ago, Kaetemi said:

if (5 < x < 10) { }

Clearly you're using the wrong language ?

From https://docs.python.org/3/reference/expressions.html?highlight=comparison#comparisons

Quote

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

 

5 hours ago, Oberon_Command said:

I'd be fine with just automatically calling operator== on each member as the default. We already get that with the implicit copy constructor/assignment operators. If the default doesn't do what I want, I'll override it, but most classes I work with are just doing that, anyway. It's the most usual case and therefore should be the default and automatic one.

My assumption is that the standards committee, being highly concerned with the implementation of the standard libraries, often encounters cases where this is not the default situation. It seems to me that, in general, there is a distinct trend of the standards committee prioritizing library authors when adding new features and rules.

Or perhaps this is an aspect of the "zero cost abstractions" philosophy and the committee doesn't want to force (what I consider) the reasonable default on everyone on the grounds that it would be a default with potential runtime costs. But... we already have that, with copy constructors... And now I'm just speculating.

My expectation here as a C++ programmer would be deep equality, because that's how the standard library types generally behave and how the vast majority of user types I have encountered in production code behave. If I wanted identity rather than equality, I would compare the pointers or similar - eg. handles, in cases where my data is double-buffered or relocatable. If you want to do something else in your own idiom, that's fine, that's why we have operator overloading, but in C++ I'm always going to expect a deep comparison and you should specifically tell me when the type is doing something different.

IMO operator overloading should be reserved for non-default cases and should not be required for holding the compiler's hand through the default ones.

Unless I misunderstand you're getting exactly that, plus auto-generated ordering: https://en.cppreference.com/w/cpp/language/default_comparisons

2 hours ago, Eternal said:

Unless I misunderstand you're getting exactly that, plus auto-generated ordering: https://en.cppreference.com/w/cpp/language/default_comparisons

To be clear, what I'm actually looking for is to be able to do this:


struct Point {
  int x;
  int y;
};

Point a = get_point_a();
Point b = get_point_b();
if (a == b) {
  // ...
}

Which currently I cannot, because operator== is not defined automatically. I want the compiler, for simple types, generate an implicit operator== just as it does an implicit copy constructor, without my having to overload anything. Furthermore, my argument is that the reasonable default is member-by-member comparisons.

8 hours ago, Kaetemi said:

I can agree with that. How about, assuming a strongly typed typedef mechanism is available, and if I'd typedef `char*` as `cstring`. As the pointer is now "hidden" from the programmer, would it be more acceptable to overload `==` on that `cstring` type?

A strong typedef doesn't actually change the type, though. It would just disallow implicit conversions with the type it aliases. So no, I wouldn't want a deep compare operator== in that case, because the actual type of cstring is still a pointer type and it still behaves like one in all other respects. "C-string" means "pointer to string." If you wanted to write a wrapper type that did deep equality, that would be fine with me, but then I'd say it should be called something else, like "weak_string" or "string_view" (which we have in the standard!).

8 hours ago, Kaetemi said:

When comparison operators are interpreted from this `(5 < b < 10)` into `((5 < b) < 10)`, which I would argue is useless (comparing a boolean with 10), rather than doing a chained expansion into the more useful `(5 < b && b < 10)`.

I doubt that's really going away any time soon. Booleans being implicitly castable to integers is probably necessary for C compatibility. I suspect there exists at least one "load-bearing bug" that depends on this behavior and changing it would break somebody's code.

1 hour ago, Oberon_Command said:

To be clear, what I'm actually looking for is to be able to do this:



struct Point {
  int x;
  int y;
};

Point a = get_point_a();
Point b = get_point_b();
if (a == b) {
  // ...
}

Which currently I cannot, because operator== is not defined automatically. I want the compiler, for simple types, generate an implicit operator== just as it does an implicit copy constructor, without my having to overload anything. Furthermore, my argument is that the reasonable default is member-by-member comparisons.

That's fair, and I don't know why implicit definition of operator== didn't make it, it was proposed at some point. I wouldn't be too surprised if it was just lower priority and will appear in C++23.

On 8/17/2019 at 9:19 PM, Oberon_Command said:

A strong typedef doesn't actually change the type, though. It would just disallow implicit conversions with the type it aliases. So no, I wouldn't want a deep compare operator== in that case, because the actual type of cstring is still a pointer type and it still behaves like one in all other respects. "C-string" means "pointer to string."

Technically `struct cstring { char *str; }` is also still just a pointer to a string, but I can overload those operators.. How does it differ from a strong typedef?

On 8/17/2019 at 9:19 PM, Oberon_Command said:

I suspect there exists at least one "load-bearing bug" that depends on this behavior and changing it would break somebody's code.

The proposal I linked earlier (not by an unknown name) is recommending to actively break compatibility on that particular syntax. They found that, in the nearly all of code where the syntax occurs, it's actually a bug, which would be fixed by introducing chaining.

On 8/17/2019 at 11:13 PM, Eternal said:

That's fair, and I don't know why implicit definition of operator== didn't make it, it was proposed at some point. I wouldn't be too surprised if it was just lower priority and will appear in C++23.

I thought that implementing the default <=> operator would automatically implement all the other comparison operators?

The syntax for implementing your own <=> is hideous, though.. They designed it to support all the cases where <=> isn't really the right operator to use (while trying to please everyone with academic correctness)... JS-style || chaining would be so much prettier.

Lots of interesting thoughts in this thread...  I'm apparently super far behind on C++ language enhancements!

Right now I'm struggling a bit with the STL and what seem to me like a lot of oddly named classes and methods.  I first learned C/C++ in the mid-90s, and switched to Java not long after.  Now that I'm back getting my hands dirty again in C++, the STL kind of feels like a mess.  My current brain freeze is with iterating over maps.  It's really "first" to access the key, and "second" to access the value?  Yikes...

Senior software developer with a passion for games, still hoping to break into the industry after all these years...

map doesn't quite exist, it's more a set of pairs, where apparently the idea "pair" won over the idea "key/value".

All languages have such quircks I think, I am still amazed that Java managed to get "array.length", "string.length()" and "(list|set|map).size()" in one language that aims to be friendly to the programmer.

 

On 8/21/2019 at 1:41 AM, Kaetemi said:

 

On 8/17/2019 at 5:13 PM, Eternal said:

That's fair, and I don't know why implicit definition of operator== didn't make it, it was proposed at some point. I wouldn't be too surprised if it was just lower priority and will appear in C++23.

I thought that implementing the default <=> operator would automatically implement all the other comparison operators?

The syntax for implementing your own <=> is hideous, though.. They designed it to support all the cases where <=> isn't really the right operator to use (while trying to please everyone with academic correctness)... JS-style || chaining would be so much prettier.

Yes, you do get all comparison operators if you implement/default operator <=>, but Oberon's point was that the default <=> should also be implicit, like copy assignment for example.

Can you elaborate what you mean by JS-style || chaining and how it would help make things cleaner? I'm very unfamiliar with JS.

 

7 hours ago, rileyman said:

Lots of interesting thoughts in this thread...  I'm apparently super far behind on C++ language enhancements!

Right now I'm struggling a bit with the STL and what seem to me like a lot of oddly named classes and methods.  I first learned C/C++ in the mid-90s, and switched to Java not long after.  Now that I'm back getting my hands dirty again in C++, the STL kind of feels like a mess.  My current brain freeze is with iterating over maps.  It's really "first" to access the key, and "second" to access the value?  Yikes...

I think you'll find lots of agreement on the odd naming (vector? map / unordered_map? iota? C++20's co_derp?). But at this point we're stuck with it.

For the map thing: In most cases, if you don't need ordering, you want a hash map aka unordered_map (or a non-standard replacement), not map. Ideally unordered would be the default. Then for iteration, the "modern" way would be to use structured bindings like this:


for(const auto&[key, value] : my_map)
{
  std::cout << key << " : " << value << "\n";
}	
	

I am seriously looking for an alternative to C++. Right now i am struggling with the complexity and footguns resulting from implicit things, attempts of the compiler to generate stuff it wasn't meant to, and the anticipation that it might even get worse in the future.

There are a lot of "safer" languages around. A colleague mentioned Rust in the C#/C++ thread. I spent a few days browsing through the language's documentation. Which is tedious because every chapter eats up half of my attention speaking of other languages and that Rust is unique and super fast and better and whatnot before it comes to the part with the real information. Too much world vision, i am too old to run after gurus. My rusty resumé: i don't like someone telling me that i can't count to 8 and so he won't let me. Also, the syntax is so different, i'd have to unlearn a lot of things i just learned from C and C++.

I now (well yesterday) started with a low overfly of D. "No ! Garbage collection !" some may cry in angst and rage. But, hey, one can switch it all off. If i get through it (one problem i see is that there is no multiple inheritance, i'd have to redesign something) and it runs fast enough, i will swap my little framework to D. If not, i'll be back to C++, having lost another month or two ...

This topic is closed to new replies.

Advertisement