Putting the 'role' back in role-playing games since 2002.
Donate to Codex
Good Old Games
  • Welcome to rpgcodex.net, a site dedicated to discussing computer based role-playing games in a free and open fashion. We're less strict than other forums, but please refer to the rules.

    "This message is awaiting moderator approval": All new users must pass through our moderation queue before they will be able to post normally. Until your account has "passed" your posts will only be visible to yourself (and moderators) until they are approved. Give us a week to get around to approving / deleting / ignoring your mundane opinion on crap before hassling us about it. Once you have passed the moderation period (think of it as a test), you will be able to post normally, just like all the other retards.

Which programming language did you choose and why?

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
I suppose std::tuple/variant are not adequate product/sum types for your use cases?

It is, sort of, but it's not really a first party citizen IMO.
As you say, if it was really tightly coupled with the language, we would get pattern matching and destructuring with a nice syntax, and that would be sweet !
 

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
Rust definitely has issues, but with "unsafe" blocks you just revert to anything you can write in C.

No, you can't violate borrowck in unsafe blocks. You have to use contrived constructs to do what you want. It's like saying you can use manual memory management in C# unsafe blocks. Not practical.
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
Rust definitely has issues, but with "unsafe" blocks you just revert to anything you can write in C.

No, you can't violate borrowck in unsafe blocks. You have to use contrived constructs to do what you want. It's like saying you can use manual memory management in C# unsafe blocks. Not practical.
You can't violate the borrow checker for outer scopes of course, but you can do pretty much anything dirty with slices of u8. But I admit that it is "contrived".
After all you can do FFI.
 
Last edited:

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
You can't violate the borrow checker for outer scopes of course, but you can do pretty much anything dirty with slices of u8. But I admit that it is "contrived".
After all you can do FFI.

That's the problem with ML, Haskell, Rust and the like. You need someone's blessing.
 

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
Agreed but it's "only" a problem for us developers. Users gain more secure and robust software :)

If you get your types to unify in C++, it's as correct or even more correct. Since C++ types are Turing-equivalent then you can perform more checks.
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
That's the problem with ML, Haskell, Rust and the like. You need someone's blessing.
If you get your types to unify in C++, it's as correct or even more correct. Since C++ types are Turing-equivalent then you can perform more checks.

Could you elaborate this a bit, please ?

PS: In C++ you've got someone's curse. Implicit conversions for integral types and lack ootb of bound checkings in arrays and std::vector.
I'll take a sound type system anytime.
The whole C++ ecosystem relies on warnings for someone to be able to approximate decent safety.
 
Last edited:

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
Could you elaborate this a bit, please ?

Make your own invariants expressed as types' template parameters and assert runs during monomorphization. This isn't an academic example, I do this in real projects. These fancy languages don't monomorphize and that is why you can't get nice things in them.

Implicit conversions for integral types

Compiler warnings make this irrelevant. Same with floating/integral conversions and between floating types.

lack ootb of bound checking

Most of the time the bugs I make are high-level architectural errors or single-procedure logic errors. Memory-related stuff is barely ever a problem. There are tools for that anyway.
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
Make your own invariants expressed as types' template parameters and assert runs during monomorphization. This isn't an academic example, I do this in real projects. These fancy languages don't monomorphize and that is why you can't get nice things in them.
Rust does monomorphization so maybe I don't get what you're saying there.
Anyway type systems are not expressive enough to encode all sophisticated invariants.
And we don't have time to write proofs, do we? Yet as users, we'd like OpenSSL and OpenSSH to be formally proved, it's immensely expensive to do so.

Compiler warnings make this irrelevant. Same with floating/integral conversions and between floating types.

There we disagree a bit. I think this should be in the standard. Of course everybody would be crying. Lots of people are crying all the time because "warnings get in their way" (and I hate them. Edit: the people, not the warnings).
Edit: if you're doing code that is cross clang, gcc, msvc and armcc, the warnings are somewhat different. So one more argument for them to be standard, IMO.

Most of the time the bugs I make are high-level architectural errors or single-procedure logic errors. Memory-related stuff is barely ever a problem. There are tools for that anyway.

Yes, I keep hearing this. Then I do code reviews, for production code, sometimes (but rarely) security code.
And industry-wide numbers suggest otherwise.
Memory errors and parallelism errors happen a lot in the wild. So I disagree again :)

I don't know what tools you're referring to, but Valgrind and sanitizers are not usable/practical in lots of embedded environments, for instance, especially with realtime contraints.

Interesting discussion, though !
I think it all boils down to "trust the implementer" vs "trust the ecosystem". I've written enough bugs to support the second stance !
 
Last edited:

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
Anyway type systems are not expressive enough to encode all sophisticated invariants.
And we don't have time to write proofs, do we? Yet as users, we'd like OpenSSL and OpenSSH to be formally proved, it's immensely expensive to do so.

That's not what I said. I encode domain-specific information in the types and do context-specific checks. That has nothing to do with proofs. In C++ types are made for whatever I want.

Edit: if you're doing code that is cross clang, gcc, msvc and armcc, the warnings are somewhat different. So one more argument for them to be standard, IMO.

Maybe. It's not a problem with correctness though.

Memory errors and parallelism errors happen a lot in the wild.

So there we have choices:

1. Valgrind, sanitizers
2. GC
3. borrowck

I'd say 1, 3, 2 in order of preference.

Valgrind and sanitizers are not usable/practical in lots of embedded environments, for instance, especially with realtime contraints.

Then we don't have to actually care about most types of memory errors, since malloc on embedded realtime isn't a thing.

I think it all boils down to "trust the implementer" vs "trust the ecosystem". I've written enough bugs to support the second stance !

Trust no one really, not even myself. Cheers!
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
Anyway type systems are not expressive enough to encode all sophisticated invariants.
And we don't have time to write proofs, do we? Yet as users, we'd like OpenSSL and OpenSSH to be formally proved, it's immensely expensive to do so.

That's not what I said. I encode domain-specific information in the types and do context-specific checks. That has nothing to do with proofs. In C++ types are made for whatever I want.

Oh so you mean embedding runtime checks in your types for instance ?
Anyway, this is too abstract.
I'll keep my opionion that first-order sum types ala Haskell or Rust are superior to the equivalent you'll build in C++ from templates and enums. And it's very helpful to the optimizer.

Edit: if you're doing code that is cross clang, gcc, msvc and armcc, the warnings are somewhat different. So one more argument for them to be standard, IMO.

Maybe. It's not a problem with correctness though.

IMO, it is. If a subset of warnings is not in the intersection, it means some people won't see it if they don't try all the compilers.
Either this subset is useless => problem. Either it is useful => worse problem.

Memory errors and parallelism errors happen a lot in the wild.

So there we have choices:

1. Valgrind, sanitizers
2. GC
3. borrowck

I'd say 1, 3, 2 in order of preference.[/QUOTE]

Totally agree about GC being the last :)
This said, regarding parallelism, 1 is way inferior to 3. They choke on lots of practical things.
And there's the problem of code coverage. I know the theory of testing all the code paths but... never saw it in practice.
Don't want a CVE that a static ananlysis tool would have caught.

Valgrind and sanitizers are not usable/practical in lots of embedded environments, for instance, especially with realtime contraints.

Then we don't have to actually care about most types of memory errors, since malloc on embedded realtime isn't a thing.

Most types, yes.

Trust no one really, not even myself. Cheers!

The best stance ! I'm happy not to write life-critical code.
 

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
Oh so you mean embedding runtime checks in your types for instance ?
Anyway, this is too abstract.

For instance, matrix struct that only exposes vector ops if it's a vector or a column vector. Or a datagram struct based on enums that checks for padding or duplicate tags.

I'll keep my opionion that first-order sum types ala Haskell or Rust are superior to the equivalent you'll build in C++

That's not an opinion, that's a fact. I can't build sum types without exhaustive pattern matching. C++ is so behind the times. I made a macro of that old Javascript trick, "[&]() { __VA_ARGS__ }()", named it "begin". Hell, Perl had this as a feature 15 years ago.
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
Oh so you mean embedding runtime checks in your types for instance ?
Anyway, this is too abstract.

For instance, matrix struct that only exposes vector ops if it's a vector or a column vector. Or a datagram struct based on enums that checks for padding or duplicate tags.

Oh, ok, some of my colleagues are fans of this kind of stuff.
I thnk this is cool if that doesn't make the code unreadable/unmaintainable.
For matrices, I think this is overrated. They end up trying to decay submatrixes magically to vectors or scalars and it's a huge machinery.
I prefer simpler APIs but it's a matter of taste.

For building datagrams (or DMA packets), this is very useful indeed.
 

Krice

Arcane
Developer
Joined
May 29, 2010
Messages
1,330
Implicit conversions for integral types and lack ootb of bound checkings in arrays and std::vector.
I'll take a sound type system anytime.
The whole C++ ecosystem relies on warnings for someone to be able to approximate decent safety.

You don't have to use raw arrays in C++. It's quite easy to write your own arrays with bounds checking and I learned that it's something you want to do, because it's often difficult to keep track of the index to the array. Raw arrays are a weak point in C and C++, so why not take them out. Pointer issues are another, but they too can be fixed by following strict ownership rules.
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
Yes, you can totally make C or C++ safe by following rules. But assembly too :)
When they designed std::vector::operator[] not to bound check, they just made C++ as bad as C regarding safety.
And people, even experts, keep making bugs that could be avoided, so we're doing something wrong.
 

GarrisonFjord

Guest
Iirc, the specification of operator[] for containers,
does not limit implementations (to check or not to check).
And debug versions of the STL can & do check e.g. iterators.
In principle this is ideal, in this particular case
(checked in debug builds, unchecked in release builds).
 

Krice

Arcane
Developer
Joined
May 29, 2010
Messages
1,330
not to bound check, they just made C++ as bad as C regarding safety.
And people, even experts, keep making bugs that could be avoided, so we're doing something wrong.

I don't know how people usually use std::vector, but I'm using it more like a simple list than an array myself and so avoiding lot of out-of-bounds errors. Everyone makes bugs, but in some cases you can prevent them from ever happening which I think is nice. I remember there was a moment when I realized you need to forget your ego which gives you all kinds of "ideas" how to code and they are "obviously" the right way to do things. When you get over it and start to learn programming it's going to be easier at least with bugs.
 

Tramboi

Prophet
Patron
Joined
May 4, 2009
Messages
1,226
Location
Paris by night
Iirc, the specification of operator[] for containers,
does not limit implementations (to check or not to check).
Oob [] is "undefined behaviour".
You don't build reliable software on "undefined behaviours".
Ada or Rust bound check by default, this is what should be done in C/C++. Unchecked accesses should be opted in.

And debug versions of the STL can & do check e.g. iterators.
In principle this is ideal, in this particular case
(checked in debug builds, unchecked in release builds).
This s not ideal because it's the root cause for lots of security problems in our world.
 

GarrisonFjord

Guest
Don't disagree, everyone says C++ gets the defaults wrong.
(I meant: having debug-checked/release-unchecked, is ideal.)

(
Example of what I referred to:
gcc 8.4.0, compiling with -D_GLIBCXX_DEBUG
vector<int> v{1,2,3,5,7};
cout << v[0] << ' ' << v[25] << '\n';
Error: attempt to subscript container with out-of-bounds index 25,
but container only holds 5 elements.
)
 

vlzvl

Arcane
Developer
Joined
Aug 7, 2017
Messages
195
Location
Athens
One can simply use vector::at() and avoid checking bounds, for both reading / setting values.
 

GarrisonFjord

Guest
Yes, but at() limits you:
You will always (debug&release) get the overhead of runtime checks.
 

Krice

Arcane
Developer
Joined
May 29, 2010
Messages
1,330
Ada or Rust bound check by default, this is what should be done in C/C++.

This is a great comment. Some people are just too "programming-oriented" to do anything else than get stuck into details. Like did you know the authors of Smalltalk (that pure OOP language) are still butthurt that C++ took OOP stuff and made it actually something practical. Then there are guys like that swedish fat boy (when I see his face I want to punch it so hard his funny hat flies off and then I want to trample that hat to the ground) who coded Minecraft with crappiest language ever Java. Now he never has to work again and is probably getting his balls sucked dry by a russian escort lady while we talk about programming languages.
 

Twiglard

Poland Stronk
Patron
Staff Member
Joined
Aug 6, 2014
Messages
7,240
Location
Poland
Strap Yourselves In Codex Year of the Donut
Like did you know the authors of Smalltalk (that pure OOP language) are still butthurt that C++ took OOP stuff and made it actually something practical.

They have every reason to be upset now that REPLs are abandoned and VM has lost its original sophisticated meaning. Top-down REPL stuff is good but only Squeak and SLIME remain.
 

As an Amazon Associate, rpgcodex.net earns from qualifying purchases.
Back
Top Bottom