Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
FlapYoJacks
Feb 12, 2009
in C/C++, always be returning -ERRNO on error if returning an int, and 0 if successful.

FlapYoJacks fucked around with this message at 23:22 on Apr 12, 2022

Adbot
ADBOT LOVES YOU

tak
Jan 31, 2003

lol demowned
Grimey Drawer

Foxfire_ posted:

Hot Take: exceptions were a mistake and errno style reporting is much better for producing reliable software than "every call can potentially throw anything, hope someone documented every type it can directly or indirectly throw and kept it up to date"

Like to write a python program that writes to a file and handles all errors involves a spelunking expedition into the CPython implementation because the documentation isn't going to tell you what those calls can throw

:hmmyes:

Cleaner, more elegant, and wrong

Cleaner, more elegant, and harder to recognize

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Having a wrapper that forces you to explicitly check whether things are okay is better than something like errno which is totally optional to actually look at.

ultrafilter
Aug 23, 2007

It's okay if you have any questions.


In C++ std::variant works well as the return type of a function that might encounter errors.

Volmarias
Dec 31, 2002

EMAIL... THE INTERNET... SEARCH ENGINES...

Xarn posted:

Yes, except it isn't guaranteed to work in C

Not sure if there's anything out there that actually is guaranteed to work with C though...

qsvui
Aug 23, 2003
some crazy thing
Exceptions are good for exiting out of a lot of code quickly.

ExcessBLarg!
Sep 1, 2001

duck monster posted:

Just had the new guy at work decided to rewrite a NodeJS UDP server in loving PHP, because "NodeJS cant scale".
What's your interviewing process like? Might need to add "write toy UDP server" to it.

Vanadium
Jan 8, 2005

Foxfire_ posted:

Hot Take: exceptions were a mistake and errno style reporting is much better for producing reliable software than "every call can potentially throw anything, hope someone documented every type it can directly or indirectly throw and kept it up to date"

Like to write a python program that writes to a file and handles all errors involves a spelunking expedition into the CPython implementation because the documentation isn't going to tell you what those calls can throw

Checked exceptions were right all along.

redleader
Aug 18, 2005

Engage according to operational parameters

Foxfire_ posted:

Hot Take: exceptions were a mistake and errno style reporting is much better for producing reliable software than "every call can potentially throw anything, hope someone documented every type it can directly or indirectly throw and kept it up to date"

Like to write a python program that writes to a file and handles all errors involves a spelunking expedition into the CPython implementation because the documentation isn't going to tell you what those calls can throw

you're the first person i've seen saying 'a global, non-thread-safe variable you need to remember to check after every single function call*' is good, actually

* don't forget to read the docs to find out what actually sets errno!

nielsm
Jun 1, 2009



redleader posted:

you're the first person i've seen saying 'a global, non-thread-safe variable you need to remember to check after every single function call*' is good, actually

* don't forget to read the docs to find out what actually sets errno!

It actually is thread safe (thread local) since 1996 or there about.

redleader
Aug 18, 2005

Engage according to operational parameters

nielsm posted:

It actually is thread safe (thread local) since 1996 or there about.

ah, ok. my bad!

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Never worked seriously with C - what's the point of having function return an int != 0 to signal error, and then write the error code into a global int variable, instead of just returning the error code in the first place?

And if the function doesn't return an error code but still set errno, doesn't that basically have all the downsides of exceptions?

Qwertycoatl
Dec 31, 2008

NihilCredo posted:

Never worked seriously with C - what's the point of having function return an int != 0 to signal error, and then write the error code into a global int variable, instead of just returning the error code in the first place?

And if the function doesn't return an error code but still set errno, doesn't that basically have all the downsides of exceptions?

Functions which set errno mostly don't return an int != 0 to signal error, they return some actual value (eg fopen() returns a file handle and if it's NULL you can check errno to find out why). Returning multiple values is a bit annoying to do in C, so they did this.

Xarn
Jun 26, 2015

nielsm posted:

It actually is thread safe (thread local) since 1996 or there about.

It still sucks massive rear end and sin, cos, and friends setting errno is an amazingly stupid design decision.



redleader posted:

* don't forget to read the docs to find out what actually sets errno!

For bonus points, your stdlib might actually touch errno when it shouldn't, zeroing it out even though an error happened in a lower level :v:

ToxicFrog
Apr 26, 2008


Foxfire_ posted:

Hot Take: exceptions were a mistake and errno style reporting is much better for producing reliable software than "every call can potentially throw anything, hope someone documented every type it can directly or indirectly throw and kept it up to date"

Like to write a python program that writes to a file and handles all errors involves a spelunking expedition into the CPython implementation because the documentation isn't going to tell you what those calls can throw

This take is both thermonuclear and wrong.

It's true that if you have unchecked exceptions, "any call can potentially throw anything" and you are at the mercy of the documentation to know what actual subset of the error space you need to handle. However, if you fail to handle every possible case, when an unhandled error occurs the program crashes with a stack trace, which is (a) obvious (b) potentially useful for debugging and (c) prevents further damage from occurring.

Meanwhile, in errno-land, any call can still potentially fail and set errno, and you still need to trust the documentation to comprehensively list every possible errno value it sets (and what it actually means in practice for that call, since some of them are pretty overloaded like EINVAL), but in addition to this, it's a lot easier for intermediate code to accidentally whack errno, and if your error handling code is missing or not comprehensive you have just summoned the ghost of ON ERROR RESUME NEXT to haunt your program. Have fun debugging a failure that only occurs because three stack frames ago you forgot to properly bracket one of your calls to sin with the correct forms of feclearexcept and fetestexcept, especially if the value of errno has gotten overwritten since the original error occurred!

(I think you could make a case for option types often being better, and in the case where you explicitly want to die instantly on error you can just use my_option.unwrap() or your language's nearest equivalent, although even then I'm not confident they're universally superior to exceptions. But there's a big difference between proper option types and errno.)

ExcessBLarg!
Sep 1, 2001
After some 20 years of systems programming I've come to generally prefer exceptions, certainly in application code. For most small applications, failing fast is the right behavior and a default exception handler is totally fine. This is especially so with language constructs like try-with-resources, context managers, RAII, etc., which automatically cleanup resources without having to explicitly code the error pathway.

Even for programs in which fast failing isn't appropriate, you often have one request-level general exception handler. In my experience, handling specific exceptions is usually rare, and even then it's unusually to just log and drop them. All this is to say is that exceptions are often straightforward to reason about.

C's error handling is conceptually simple but easily one of the weakest parts of the language, given that you can ignore error situations outright. Ideally C APIs will tolerate repeated errors by doing nothing (e.g., can't acquire a file descriptor returns -1, and passing -1 as a fd to any subsequent operation results in an EBADF). Many APIs fall short of this and without explicit error coding will segfault or worse.

Nearly all C code I write begins every statement with "if (....)". Since there's no RAII, a lot of times the error pathway is a goto to a set of cleanup labels after the regular end of the function (a common C idiom). And that's not even getting into the POSIX vs. errno and other error reporting methods.

BigPaddy
Jun 30, 2008

That night we performed the rite and opened the gate.
Halfway through, I went to fix us both a coke float.
By the time I got back, he'd gone insane.
Plus, he'd left the gate open and there was evil everywhere.


Having to explain to non SFDC technical people that I wrap everything in a try catch because the standard error reporting on force.com is trash only for them to argue that I should only be checking for specific exceptions on actions I am taking and not the generic catch all exception gets old. Yes in Python/Java/Ruby/etc… you can handle unexpected issues on the platform that is calling the application but I can’t do that on force.com as I do not have access to that level to do such handling, it is more akin to say Lambda functions but with less logging and configuration available. So we can have stuff silently fail due to some system level issue that we have no control over such as any of the seven dwarves* database errors or I can just catch everything and send the error in real-time to an error reporting tool that isn’t an email that might be sent to whoever is setup to receive them and most of the time do not work at the company anymore. Usually this is enough but you always have that software engineering grad who is a year or two out of school who chooses this hill to die on.

* Salesforce is a platform built over oracle and the layer between oracle and the customer platform has errors named after the seven dwarves that are utterly useless unless you have knowledge of how the platform works which defeats the point of it being “no software”.

Sagacity
May 2, 2003
Hopefully my epitaph will be funnier than my custom title.

ExcessBLarg! posted:

C's error handling is conceptually simple but easily one of the weakest parts of the language, given that you can ignore error situations outright.
It's such a poor concept that Go solved it by replacing the concept entirely with conventions that still allow you to easily ignore error situations

D34THROW
Jan 29, 2012

RETAIL RETAIL LISTEN TO ME BITCH ABOUT RETAIL
:rant:

BigPaddy posted:

* Salesforce is a platform built over oracle and the layer between oracle and the customer platform has errors named after the seven dwarves that are utterly useless unless you have knowledge of how the platform works which defeats the point of it being “no software”.

They only need one such error to describe themselves and another to describe me upon finding this out :wtf:

Absurd Alhazred
Mar 27, 2010

by Athanatos
It's hilarious for a Microsoft dev to complain about exceptions when if you dig deep enough, even API calls that end up returning an HRESULT are implemented through exception handling internally.

CPColin
Sep 9, 2003

Big ol' smile.
Self-horror:
code:
    /**
     * Returns `true` if [httpSession] can be used without throwing exceptions because there's no current request.
     */
    fun isHttpSessionAvailable() = try {
        httpSession.attributeNames

        true
    } catch (_: java.lang.IllegalStateException) {
        false
    }
It's ugly, but only takes a handful of microseconds, so gently caress it. (much like my wiener)

Foxfire_
Nov 8, 2010

ExcessBLarg! posted:

For most small applications, failing fast is the right behavior and a default exception handler is totally fine.
...
Even for programs in which fast failing isn't appropriate, you often have one request-level general exception handler. In my experience, handling specific exceptions is usually rare, and even then it's unusually to just log and drop them.

I don't think this is true, at least for exception-heavy languages. Like in python for example, all of these throw:
- you tried to open a file that doesn't exist for reading
- you tried to write to a file, but disk is full or you don't have permissions
- a top connection was dropped somewhere in the internet
Those aren't situations where a robust application should log a stack trace and terminate.

An operation that can fail at least conceptually has a (ok/failed, result if successful, information about error if failed). It's best if all that is returned by the call (that's what rust does, it intentionally doesn't have exceptions because it's designers also think they're bad), but errno-style where the call returns (ok/failed, result if successful) and (information about error if failed) is stashed in a shared location is less bad than exceptions where it's in a nonobvious, potentially nonlocal place

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

Foxfire_ posted:

I don't think this is true, at least for exception-heavy languages. Like in python for example, all of these throw:
- you tried to open a file that doesn't exist for reading
- you tried to write to a file, but disk is full or you don't have permissions
- a top connection was dropped somewhere in the internet
Those aren't situations where a robust application should log a stack trace and terminate.

The first two of these situations are ones where the program shouldn't do anything to try and recover on its own, it should just abandon whatever operation it was attempting and alert the user so that the user can fix the problem before trying again from the beginning.

The last one is correctly handled by abandoning the operation that was being attempted and retrying it from the beginning.

This style of control flow is very easy to accomplish with exceptions, somewhat easy to accomplish with well-designed wrapper objects and language constructs to support them, and obnoxiously painful if you have to manually check errno.

ExcessBLarg!
Sep 1, 2001

Foxfire_ posted:

Those aren't situations where a robust application should log a stack trace and terminate.
I mean "application" in the Unix "does one thing well" tradition in which case read or write errors should be fast fail and connection termination is often handled by a supervisor process/automatic respawning.

At least, fast fail is a reasonable starting point and you can make your application robust to common errors for efficiency. Like if your database connection regularly falls over you might want to implement/enable retries in the database driver.

GUI applications are a different animal. There you would typically have an exception handler that backs any UI-invoked operation to report the error to the user.

ExcessBLarg!
Sep 1, 2001
Quote <> Edit.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

ExcessBLarg! posted:

I mean "application" in the Unix "does one thing well" tradition in which case read or write errors should be fast fail and connection termination is often handled by a supervisor process/automatic respawning.

At least, fast fail is a reasonable starting point and you can make your application robust to common errors for efficiency. Like if your database connection regularly falls over you might want to implement/enable retries in the database driver.

GUI applications are a different animal. There you would typically have an exception handler that backs any UI-invoked operation to report the error to the user.

Bubbling up to the event loop and reporting an error to the user in a GUI program is basically the same thing as printing an error and exiting in a CLI program. You have to actually clean up because your "supervisor" is in the same process rather than being a parent process, but it's otherwise very similar in both concept and implementation,

Zopotantor
Feb 24, 2013

...und ist er drin dann lassen wir ihn niemals wieder raus...

Jabor posted:

The first two of these situations are ones where the program shouldn't do anything to try and recover on its own, it should just abandon whatever operation it was attempting and alert the user so that the user can fix the problem before trying again from the beginning.

The last one is correctly handled by abandoning the operation that was being attempted and retrying it from the beginning.

This style of control flow is very easy to accomplish with exceptions, somewhat easy to accomplish with well-designed wrapper objects and language constructs to support them, and obnoxiously painful if you have to manually check errno.

When saving data in an interactive program, failing by terminating is not an option if you want to keep making sales. You must at least let the user try to fix the problem (pick a different location, make space on the disk etc.) and retry, instead of throwing away a potentially large amount of work.
This includes avoiding things that can fail (e.g., not allocating more memory) while saving.

e: I think that’s what you are saying actually, didn’t read carefully enough.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Yeah, that's what I'm saying. The software should abandon the save attempt, tell the user what went wrong, and then just wait for the user to fix it and try saving again. What's not correct is to randomly segfault because you forgot to check the return value of one sub-operation and now something is NULL that you didn't expect to be NULL.

Exceptions make it very easy to have a programming model of "if something goes wrong, I don't care what the problem was, here's what I'm going to do about it". Error codes kind of force you to say "If this goes wrong I'll do this, if this other thing goes wrong I'll do this also, if this third thing goes wrong I will also do this", and makes it much easier to accidentally not handle one specific thing correctly.

Sagacity
May 2, 2003
Hopefully my epitaph will be funnier than my custom title.

Jabor posted:

Error codes kind of force you to say "If this goes wrong I'll do this, if this other thing goes wrong I'll do this also, if this third thing goes wrong I will also do this", and makes it much easier to accidentally not handle one specific thing correctly.
How do you know which error codes you can get back? There's no support for any kind of hierarchy, whereas with exceptions you can at least have a 'catch all exceptions of this type' handler.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

CPColin posted:

Self-horror:
code:
    /**
     * Returns `true` if [httpSession] can be used without throwing exceptions because there's no current request.
     */
    fun isHttpSessionAvailable() = try {
        httpSession.attributeNames

        true
    } catch (_: java.lang.IllegalStateException) {
        false
    }
It's ugly, but only takes a handful of microseconds, so gently caress it. (much like my wiener)

Since this is Kotlin so you already get null checks for free, I'm guessing that somehow httpSession throws that exception whenever accessing it despite it not being null? If so, that's thread-worthy.

CPColin
Sep 9, 2003

Big ol' smile.
Correct. Spring will autowire an HttpSession instance for you, even when no session exists. Doing any operation on that object will immediately blow up with an IllegalStateException and there's no isPresent() equivalent that I could find.

ultrafilter
Aug 23, 2007

It's okay if you have any questions.


https://twitter.com/bernhardsson/status/1515139078630645762

I knew floating point was a horror but I thought integers were pretty safe.

Jeb Bush 2012
Apr 4, 2007

A mathematician, like a painter or poet, is a maker of patterns. If his patterns are more permanent than theirs, it is because they are made with ideas.
trying to do arithmetic on signed integers near the overflow/underflow boundary is very much not safe

Absurd Alhazred
Mar 27, 2010

by Athanatos

Jeb Bush 2012 posted:

trying to do arithmetic on signed integers near the overflow/underflow boundary is very much not safe

Now, now, it's not safe to do arithmetic on unsigned integers near those boundaries, either.

QuarkJets
Sep 8, 2008

ultrafilter posted:

https://twitter.com/bernhardsson/status/1515139078630645762

I knew floating point was a horror but I thought integers were pretty safe.

Integers are safe until you start interacting with them at the edge of their defined range, I mean everyone has heard of overflow right? So for a valid range of -128 to 127 it shouldn't be surprising that abs(-128) does something funny

OddObserver
Apr 3, 2009

Absurd Alhazred posted:

Now, now, it's not safe to do arithmetic on unsigned integers near those boundaries, either.

Isn't it well-defined, at least?

taqueso
Mar 8, 2004


:911:
:wookie: :thermidor: :wookie:
:dehumanize:

:pirate::hf::tinfoil:

OddObserver posted:

Isn't it well-defined, at least?

in C? :shobon:

OddObserver
Apr 3, 2009

Don't know, but in C++ IIRC it's specified to be mod 2^number of bits arithmetic. So stuff like array size computation going wrong can still happen, but if it does the wrong answer is knowable and predictable.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Unsigned overflow is well-defined in C as well. It might give undesired results if the programmer hasn't considered the possibility, but the compiler isn't allowed to do things like assume x+1>x when optimizing (unless it can actually prove that this specific instance won't overflow).

Adbot
ADBOT LOVES YOU

Xarn
Jun 26, 2015
Unsigned is in fact well defined even for platforms where the natural behaviour is different. (AFAIK this is also the original reason why signed is not defined)

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply