Live data from Hacker News

Parse, Don’t Validate – Some C Safety Tips

lelanthran.com

51–60 of 78 posts

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

#51
post #38
post #34

Earlier quoted context omitted.

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)?

There's a huge difference. One is an email_t to validate and one is a char* to validate.

  As established, head is partial because there is no element to return if the list is empty: we’ve made a promise we cannot possibly fulfill. Fortunately, there’s an easy solution to that dilemma: we can weaken our promise. Since we cannot guarantee the caller an element of the list, we’ll have to practice a little expectation management: we’ll do our best return an element if we can, but we reserve the right to return nothing at all. In Haskell, we express this possibility using the Maybe type
^ Weaken the post-condition. In some contexts null might be close enough for Maybe. But is Maybe itself even good enough?

  Returning Maybe is undoubtably convenient when we’re implementing head. However, it becomes significantly less convenient when we want to actually use it! Since head always has the potential to return Nothing, the burden falls upon its callers to handle that possibility, and sometimes that passing of the buck can be incredibly frustrating.
This is where the article falls short. It might be good (the enemy of perfect), but it ain't PdV.

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

#52

From experience, parsing input into data structures that fit the problem domain once at the "edge" is a good idea. The code becomes a lot more maintainable without a bunch of validation checks scattered all over the place, picking a data structure for the problem at hand usually leads to cleaner solutions, and errors usually show up much earlier and are easier to debug. From experience though I've found that wrapping…

I think it can be useful to think of the parsing and logic parts both as modules, with the parsing part interfacing with the outside world via unstructured data, and the parsing and logic parts interfacing via structured data, i.e.: the validated types.

From that perspective, there is a clear trade-off on the size of the parsing–logic interface. Introducing more granular, safer validated types may give you better functionality, but it forces you to expand that interface and create coupling.

I think there is a middle ground, which is that these safe types should be chunked into larger structures that enforce a range of related invariants and hopefully have some kind of domain meaning. That way, you shrink the conceptual surface area of the interface so that working with it is less painful.

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

#53
post #23

Earlier quoted context omitted.

This is validate . You made an email-or-error type and named it email_t and then manually checked it. PDV returns an non-error-email type from the check method .

I don't understand; what is your suggested solution?

I'm not smart enough to suggest a fix here, I'm just pointing out that this article is not the PdV from the well known article.

But I can spot when code is doing exactly what the cited article says not to do,

This line is the "validate" in the expression "parse, don't validate":

  if (theEmail.error != PARSE_OK)
You might like it, but that's not my business. Maybe this C article should have been "parse, then validate".

You'd be better off reading the original: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

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

#54
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…

> The whole "prevent double free" claim is completely bogus.

The way I interpreted the author's intent was that, the logic of error handling (something C sucks even more at) can be greatly simplified if your cleanup routine can freely be called multiple times. At the moment an error happens you no longer have to keep track of where you are in the lifecycle of each local variable, you can just call cleanup() on everything. I actually like the idea from that standpoint.

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

#55
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've re-read the article again since getting a bunch of up and down votes across the comment section, and I think you've chosen a better name for this article than PdV. It really is just about using newtype wrappers.

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

#56
post #43

One flaw I've seen in "Parse, Don't Validate" as it pertains to real codebases is that you end up with a combinatorial prolieration of types. E.g., requiring that a string be base64, have a certain fixed length, and be provided by the user. E.g., requiring that a file have the correct MIME type, not be too large, and contain no EXIF metadata. If you really always need all n of those things then life isn't terrible (y…

Structs are representations of combinatorial types! In your file case, you could parse the input into a struct, and then accept or reject further processing based on that contents of that struct. Of course, it would be reasonable to claim that the accept/reject step is validation, but I believe “Parse, don’t validate” is about handling input, not an admonition to never perform validation.

In pure C however, you still get the types-in-source-code explosion, for lack of parametric polymorphism. You need an email_or_error and a name_or_error, etc. The alternative is to fake PP with a void*, but that's so ugly I think I'd scrap the whole effort and just use char*.

> I believe “Parse, don’t validate” is about handling input, not an admonition to never perform validation.

It's about validation happening at exactly one place in the code base (during the "parse" - even though it's not limited to string-processing), so that callers can't do the validation themselves - because callers will validate 0 times or n>1 times.

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

#57
post #2

The trouble I have with this approach (which, conceptually, I agree with) is that it's damned hard to do anything with the parse results. Want to print that email_t? Then you're right back to char*, unless you somehow write your own I/O system that knows about your opaque conventions. So you say, okay, I'll make an `email_to_string` function. Does it return a copy or a reference? Who frees it? etc, etc, and you're ba…

Firstly, `parsing` is just a way to say "serialise from a string". The reverse operation can be done for every type you are creating. If the reverse operation (serialise to a string) does not exist in the interface then adding it gives you a single place to catch all the bugs. I'm thinking of that recent git bug that occurred because the round-trip of `string -> type -> string` had an error (stripping out the CR char…

Linguistic nit: deserialize from a string, serialize to a string

“Serialization” is the act of taking an internal data structure (of whatever shape and depth) and outputting it for transmission or storage. The opposite is “deserialization,” restoring the original shape and depth.

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

#59
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…

> The whole "prevent double free" claim is completely bogus. "Completely" means "for all". Are you seriously claiming that "for all instances of double-free, setting the pointer to NULL after freeing it would not help"?

Eeeeh, I don't think 'completely bogus' means 'exhaustively false for all situations'. It just means 'demonstrably false' (for some relatively sane example, we're talking about C after all which means there will always be bogus examples which break any given assumption). There's plenty of cases where zeroing a pointer immediately after freeing it will prevent any further issues. It's still bogus to claim that it categorically solves the problem of double frees. But it does help.

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

#60
post #23

Earlier quoted context omitted.

This is validate . You made an email-or-error type and named it email_t and then manually checked it. PDV returns an non-error-email type from the check method .

I don't understand; what is your suggested solution?

parseEmail() should either return a valid email, or not return at all; whether that means panic, exit, or jump to an error handler... is left to the implementer
Post reply on HN