3 ms·
Interesting. Gives me a better intuition for strings in zig as well. I'm curious about the reverse()-function - it requires the caller to allocate and pass in
by e12e 5y ago
Interesting. Gives me a better intuition for strings in zig as well.
I'm curious about the reverse()-function - it requires the caller to allocate and pass in the "out" buffer as a mutable (well, mutable in the sense that the buffer is written to) - yet returns a pointer at the end (rather than void, or an error code).
Is that a typical c/plan9 idiom?
I would probably prefer the function allocating and returning, or the caller allocating and the function (procedure) just writing to the buffer it got as arguments?
Rune*
reverse(Rune *in, Rune *out, usize len)
{
int i;
int to = 0, from = len-1;
while(from >= 0){
out[to] = in[from];
from--;
to++;
}
return out;
}
- kevin_thibedeau 5y agoCaller provided objects are a standard idiom that offers greater flexibility to use static/global vars, objects with a FAM, and custom allocators.
- e12e 5y agoI was more thinking about the combination of caller provided object and callee returning a pointer (in this case to the same object). But I suppose it decouples "call time" resources from "return time" resources (the current api does not guarantee that the result will be in caller's "out", just that a buffer will be returned that the caller owns and have to de-allocate/manage).
- eqvinox 5y agoThat's a common (I wouldn't necessarily call it "standard") C idiom, to allow chaining calls. You could e.g. do puts(reverse(fwdbuf, reverse(revbuf, input)));
- henesy 5y agoAuthor here. While the other comments are correct, the exact reason for this use of providing in/out is that the reversal is called on a subset of the incoming array. line = Brdstr(in, '\n', 1); will give us a null-terminated string, but we don't want to flip the null and truncate the string, so to lazily avoid that we do: rlen = runestrlen(rstr); rev = calloc(rlen+1, sizeof (Rune)); reverse(rstr, rev, rlen); so we get the number of runes in the input, add 1 for the \0, then reverse the pre-\0 characters. We could have the reverse() function allocate n+1 elements for the string and return an always null-delimited string, but then we need to pass it a string that doesn't have a \0, or make it assume that it will always get a \0 and treat that some way. Passing in both items and the number to iterate felt less noisy for a quick solution :)
- e12e 5y agoThank you, but that's still not an answer to my question :) You pass in *out as an argument (allocated by caller, so caller has a handle on it), then the result/value of reverse ends up in *out - and then reverse returns *out as a return value in addition to having done its work. I was wondering why (I guess you could say I ask why it's a "function" not a "procedure"). I guess that the contract is that "reverse" takes ownership of *out as its passed in, and just happens to not return a pointer to a different buffer. But then my question is why it can't do its own allocation too... (which you did explain). In other words - why does reverse return a pointer rather than, say a success/error (or void - can it error? Maybe on arbitrary binary data?)
- henesy 5y agoThe first version I implemented did allocate and return the allocated output buffer, but I scrapped that design I never changed the return value after I did this and didn’t see an issue at the time For the record, this program was written casually on a live stream and is hardly a textbook definition of correctness :)
- e12e 5y agoOk, thank you. I didn't mean it as criticism, just genuinely curious about the (c language) idioms involved - especially after struggling a bit with Zig strings in relation to advent of code - and after seeing the approach rust takes on ownership. In zig the idiomatic approach (as far as I can tell) is to manage allocator in main/the caller, and passing that down to the callee. Thus allocation strategy (heap/stack/arena etc) is global/caller determined, while resources are "locally" managed. I generally work in high level languages like ruby, where one rarely worries about the details of (especially string) allocation (beyond trying to not create way too many copies/buffers). So it's interesting to see what kind of balance on encapsulation/delegation of this concern can/should be found in C. Thank you for taking the time to comment.
- henesy 5y ago