Live data from Hacker News

Parse, Don’t Validate – Some C Safety Tips

lelanthran.com

31–40 of 78 posts

Re: Parse, Don’t Validate – Some C Safety Tips

#31
post #19

Earlier quoted context omitted.

> and I don't take 'pseudocode' as an excuse Weird hill to die on, since neither email_t nor PARSE_ERROR were defined in the sample snippets. How do you know PARSE_ERROR is not email_t?

It's the parse-versus-validate hill in this case. This pseudocode is "Validate" for at least 3 reasons: Forgetting to check: this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. Repeatable/redundant checks: First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter…

> It has to be for it to compile, right? Which means that email_t is the type which represents both valid and invalid emails. How do you know if it's valid? You remember to write a check for it. Why not just save yourself some keystrokes and use char* instead. This is validate, not parse.

I feel this kind of fundamentalism is letting the perfect be the enemy of the good.

Re: Parse, Don’t Validate – Some C Safety Tips

#32
post #6
post #4

Earlier quoted context omitted.

email_t doesn't have to be opaque; if it's just a visible wrapper around char* then you can still do everything with it as a char* (that is, everything you do with strings). The benefit is to avoid treating char*s as email_t, not avoiding treating email_t as char*.

(Using a thin wrapper like this to add safety is called the newtype pattern, if anyone wants to know.)

I was curious how this would look in C, and I found this article[1] how this could look in C, apparently with very little overhead.

And as I just saw, Python 3.10 also introduced a NewType[2] wrapper. I'll have to see how that feels to handle.

1: https://blog.nelhage.com/2010/10/using-haskells-newtype-in-c...

2: https://typing.python.org/en/latest/spec/aliases.html#newtyp...

Re: Parse, Don’t Validate – Some C Safety Tips

#33
post #27

Earlier quoted context omitted.

> `_t` should not be used for custom types since it's reserved for future standard types (and/or types declared in a header you might include someday). That old thing again... The _t postfix is only reserved in the POSIX standard, but not in the C standard (and C and POSIX are entirely different things - outside the UNIX bubble at least). It's unlikely that POSIX changes anymore, but if you get a name collision in a…

The C23 spec also says: > A potentially reserved identifier becomes a reserved identifier when an implementation begins using it or a future standard reserves it, but is otherwise available for use by the programmer. Which, in practice, does mean using _t is likely to cause you problems, as it may become a reserved identifier, when an implementation like POSIX begins using it.

[deleted]

Re: Parse, Don’t Validate – Some C Safety Tips

#34
post #31
post #19

Earlier quoted context omitted.

It's the parse-versus-validate hill in this case. This pseudocode is "Validate" for at least 3 reasons: Forgetting to check: this check is fragile: it’s extremely easy to forget. Because its return value is unused, it can always be omitted, and the code that needs it would still typecheck. Repeatable/redundant checks: First, it’s just annoying. We already checked that the list is non-empty, why do we have to clutter…

> It has to be for it to compile, right? Which means that email_t is the type which represents both valid and invalid emails. How do you know if it's valid? You remember to write a check for it. Why not just save yourself some keystrokes and use char* instead. This is validate, not parse. I feel this kind of fundamentalism is letting the perfect be the enemy of the good.

Every C programmer is already doing it the 'good' way (validation), so this article doesn't really add anything.

The only fundamentalism involved in PdV is: if you have an email, it's actually an email. It's not arbitrary data that may or may not an email.

Maybe you want your emailing methods to accept both emails and not-emails in your code base. Then it's up to each method to validate it before working on it. That is precisely what PdV warns against.

Re: Parse, Don’t Validate – Some C Safety Tips

#35
post #27

Earlier quoted context omitted.

> `_t` should not be used for custom types since it's reserved for future standard types (and/or types declared in a header you might include someday). That old thing again... The _t postfix is only reserved in the POSIX standard, but not in the C standard (and C and POSIX are entirely different things - outside the UNIX bubble at least). It's unlikely that POSIX changes anymore, but if you get a name collision in a…

The C23 spec also says: > A potentially reserved identifier becomes a reserved identifier when an implementation begins using it or a future standard reserves it, but is otherwise available for use by the programmer. Which, in practice, does mean using _t is likely to cause you problems, as it may become a reserved identifier, when an implementation like POSIX begins using it.

Still debatable since the C standard doesn't reserve the _t postfix (it does reserve a single leading underscore followed by a capital letter, e.g. _Bool, and IIRC it also reserves two leading underscores).

What POSIX reserves or doesn't reserve doesn't affect code that follows only the C standard but doesn't care about POSIX compatibility, and especially _t is so widely used in C libraries that POSIX's opinion obviously doesn't matter all that much in the real world.

Re: Parse, Don’t Validate – Some C Safety Tips

#36
I've been shouting this from "the rooftops" ever since I eventually independently had come to the same conclusion through sufficient volume of experience and empiry. I consider it a small tragedy, however, that my words tend to fall on deaf ears as I try to explain to e.g. co-workers why they should, in fact, parse their input and use the language's type system to their advantage, vs. all the `validateThis` and `validateThat` they seem to be copying from e.g. Stack Overflow. I see them stumbling and breaking their nose time and again passing URIs around as strings -- even in languages other than C (so this isn't obviously a problem C created and needs to solve alone) -- where it's never enforced that the URI is e.g. "absolute" or "relative" (nevermind the fact these people often don't seem to care for the difference). They do the same error with date and time (how many decades of advice do we need to accumulate and impart on our younger peers for them to irrevocably understand why e.g. date and time should never be passed around as numbers or strings, with very clearly documented and justified exceptions?). But the tragedy is never more evident when the person looks at you like you're suddenly talking Klingon to them and how bored they are with the notion and how it cannot be so important as to disturb their very important "coding project" (we don't write Knuth's "literal" programs any more -- we _code_).

Re: Parse, Don’t Validate – Some C Safety Tips

#37
post #3

`_t` should not be used for custom types since it's reserved for future standard types (and/or types declared in a header you might include someday). This does cause real-world problems (`key_t` anyone?). Gratuitous allocations are gratuitous. The whole "prevent double free" claim is completely bogus. Setting a variable to `NULL` only works for cases where there is one, obvious, owner, which is not the circumstance u…

For the _t suffix it is deeply unfortunate that on the one hand C's standard library gives people this idea but then the standard says they mustn't use it. I understand exactly why it was necessary, but to my mind that highlighted an urgent need to provide actual namespacing so that we don't need to rope off whole categories of identifiers for exclusive use by the stdlib, with the implication that every single librar…

> an urgent need to provide actual namespacing

Some newer parts of the standard library use a stdc_ prefix now (https://en.cppreference.com/w/c/numeric/bit_manip.html).

Re: Parse, Don’t Validate – Some C Safety Tips

#38
post #34
post #31

Earlier quoted context omitted.

> It has to be for it to compile, right? Which means that email_t is the type which represents both valid and invalid emails. How do you know if it's valid? You remember to write a check for it. Why not just save yourself some keystrokes and use char* instead. This is validate, not parse. I feel this kind of fundamentalism is letting the perfect be the enemy of the good.

Every C programmer is already doing it the 'good' way (validation), so this article doesn't really add anything. The only fundamentalism involved in PdV is: if you have an email, it's actually an email. It's not arbitrary data that may or may not an email. Maybe you want your emailing methods to accept both emails and not-emails in your code base. Then it's up to each method to validate it before working on it. That…

You don't think there's a degree of difference between (valid email_t or null) and (valid char pointer or invalid char pointer)?

Re: Parse, Don’t Validate – Some C Safety Tips

#39
post #32
post #6

Earlier quoted context omitted.

(Using a thin wrapper like this to add safety is called the newtype pattern, if anyone wants to know.)

I was curious how this would look in C, and I found this article[1] how this could look in C, apparently with very little overhead. And as I just saw, Python 3.10 also introduced a NewType[2] wrapper. I'll have to see how that feels to handle. 1: https://blog.nelhage.com/2010/10/using-haskells-newtype-in-c... 2: https://typing.python.org/en/latest/spec/aliases.html#newtyp...

Python’s NewType is, confusingly, a very different thing: it’s a compile-time-only subtype of the original, rather than a Haskell-style newtype (which is an entirely separate type from its source).

Re: Parse, Don’t Validate – Some C Safety Tips

#40
post #27

Earlier quoted context omitted.

The C23 spec also says: > A potentially reserved identifier becomes a reserved identifier when an implementation begins using it or a future standard reserves it, but is otherwise available for use by the programmer. Which, in practice, does mean using _t is likely to cause you problems, as it may become a reserved identifier, when an implementation like POSIX begins using it.

Still debatable since the C standard doesn't reserve the _t postfix (it does reserve a single leading underscore followed by a capital letter, e.g. _Bool, and IIRC it also reserves two leading underscores). What POSIX reserves or doesn't reserve doesn't affect code that follows only the C standard but doesn't care about POSIX compatibility, and especially _t is so widely used in C libraries that POSIX's opinion obvio…

Whilst you do point to 6.4.3 for where it does reserve "All identifiers that begin with a double underscore (__) or begin with an underscore (_) followed by an uppercase letter"... That section also has the lovely:

> Other identifiers may be reserved.

If an implementation of C uses it... Just... Don't. The standard won't save you here, because it's happy for an implementation to do whatever they feel like.

Post reply on HN