8 ms·
Using Zig's translate-C to understand weird C code
- caslon 5y agoAm I the only one who thinks that Zig's syntax looks incredibly ugly? I've never seen a snippet that didn't make my eyes glaze over from the syntax. What am I not getting here?
- deaddodo 5y agoAs someone who likes both C and Zig for their simplicity and explicitness, I don’t find it “ugly”. I do find it practical, with an aesthetically barebones approach.
- capableweb 5y ago> Am I the only one who thinks that Zig's syntax looks incredibly ugly? It's very subjective what is beautiful or ugly, of course. It'd be more interesting if you can offer specific critique rather than just calling it ugly. You're probably not the only one, I for one would call any non-lisp "ugly", but again, highly subjective, as many others find some C-like code beautiful but other C-like code ugly.
- jcelerier 5y ago> It's very subjective what is beautiful or ugly, of course. if it was, something like trypophobia would not exist
- capableweb 5y agoWhat? Are you arguing that because a phobia (that not everyone had) exists, beauty is not subjective but absolute? I'd love to see how you measure beauty if so, including the "beauty of code".
- Talanes 5y agoTrypophobia proves beauty subjective, not the other way around. I can find an image beautiful that someone with trypophobia finds deeply unsettling.
- jcelerier 5y agoObjective / subjective does not mean that things cannot be experienced relatively. Someone colourblind isn't able to distinguish all colours, yet it does not mean that colours as a frequency of electromagnetic radiation are subjective, only their experience is.
- pjmlp 5y agoYou are not alone, if I miss @ everywhere I will just start an Objective-C project. Then using modules with JavaScript AMD pattern is also not appealing.
- AnIdiotOnTheNet 5y agoIf using '@' to prefix builtins[0] is the biggest complaint[1] people have about Zig, then Zig must doing a pretty good job. [0] or really any statement at all about aesthetics [1] I know for a fact pjmlp will also complain about lack of built-in gc or ref-count pointer safety.
- pjmlp 5y agoPaying attention it seems.
- foxfluff 5y agoYeah, the syntax doesn't really excite to me too much. Which is a shame, because I would like to see a modernized "better C" that isn't more verbose than C.
- matheusmoreira 5y agoYeah. I think I'm so used to C by now that I just can't handle anything that doesn't look like C. It's like my brain just ignores text when it can't recognize the C code patterns. Semantically Zig is really interesting though.
- skocznymroczny 5y agoI feel similar when it comes to non-OOP code. I don't like free functions and global variables just hanging there.
- matheusmoreira 5y agoYeah, me neither. Names must belong to some namespace, it really bothers me when code starts binding common nouns in a global context. C lacks namespaces so I use prefixes instead. At least this solution doesn't screw up the ABI like in C++. I hate global variables so much it's one of the reasons I got rid of libc. Freestanding C turned out to be a superior language just because it lacks all the libc cruft. I ultimately dropped Ruby because of global state. It's such a wonderful language but it has one fatal flaw: lack of proper modules. The require method just executes Ruby source files, modifying the global state of the interpreter. It ceased to be a beautiful language once I realized this. Python's modules are superior, and the Javascript approach is the best one: just a normal function that returns a normal object containing exported data and functions. Javascript modules are c.ompletely reified.
- kristoff_it 5y agoOOP might have helped popularize usage of the dot notation, but namespaces are a different thing. Zig has no inheritance but everything is namespaced, including declaring functions inside struct definitions so that you can use them as if they were methods.
- cies 5y agoIt is common to make stuff you dont generally want in your code (but still need to be able to do because the language is sufficiently powerful) look ugly. Like unsafePerformIO in Haskell.
- kristoff_it 5y agoThis particular example is machine-generated code created by translate-c, it's meant to be semantically equivalent to the C code and even uses language features that you're normally not even supposed to use (c pointers). Reasonable Zig code looks more like this: https://github.com/riverwm/river/blob/master/riverctl/main.zig https://github.com/riverwm/river/blob/master/riverctl/main.z... That said I think it's fine if you don't like the syntax. I think that some complaints are honestly too superficial to be legitimate (like complaining about builtins being prefixed with @), but at the same time Zig is often times prioritizing explicitness over "good looking". I personally consider Swift a very good looking language, but then I look at all the new features that got added since I used it last, remember that I value simplicity over aesthetics, and go back to Zig.
- dragonelite 5y agoOoh that looks a bit like Rust having quickly scrolling by. I have been keeping a eye on Zig but I'm waiting till the package manager stuff has been finalized and implemented.
- WithinReason 5y agoThe author translates 1 line of C mess to 7 lines of Zig mess which are then manually simplified through transforms. Why not just apply transforms to the original C code? Or is this only for people who know Zig syntax but not C?
- mannykannot 5y agoAs someone unaware of Zig and its translate-C feature, I read the article as being about those things, not C.
- rightbyte 5y agoOr like use the cdecl cli tool ...
- dosshell 5y agoOr like use https://cppinsights.io/ https://cppinsights.io/ ...
- michaelmior 5y agoThat gives the following output which I don't think is much more readable. void f(int * a) { void * p = reinterpret_cast<void *>(&a); ***reinterpret_cast<int *(*)[]>(p) = 1; }
- cies 5y agoMaybe after several steps of manual processing it is :) (not sure what steps that should be)
- michaelmior 5y agoSure but I don't think C++ Insights really helped.
- kristoff_it 5y ago> Or is this only for people who know Zig syntax but not C? It's a blog post hosted on zig.news (so intended for an audience that knows Zig), I think we can safely assume that it's not meant to be an universal guide for understanding C code, but rather the recount of a (somewhat minor) experience lived by the author. It's even stated upfront: > @rep_stosq_void on Twitter posted this strange sample of C code, and I wanted to show my process of understanding this contrived C code. "I was presented with this code, and this is what I did to understand it"
- bruce343434 5y agoThat line is honestly trivial for anyone familiar with the spiral rule. Interpret p as a pointer to an array of pointers to integer. Since arrays decay to pointers, you triple dereference it to get 1. the array 2. the first element of that array 3. whatever int that element is pointing to Then set it to 1. It took me about 5 seconds. I have no idea what that Zig is supposed to do on the other hand. Note: the programmer who wrote that C code was being very obtuse. This is essentially just `*a = 1`, the whole p thing is some form of baroque code.
- nicoburns 5y ago> That line is honestly trivial for anyone familiar with the spiral rule. Perhaps, but the spiral rule is one of the most complicated parts of C. Most things are trivial to someone who knows how to do the thing.
- bruce343434 5y agoYou're right of course. Every language has warts, and declaration, especially involving pointers, is C's. But once you internalize that declaration mirrors usage, together with the spiral rule, it will all immediately become clear. There is some method to the madness.
- dnautics 5y agoI thought the spiral rule was a tongue-in-cheek "rule" that was just made to point out a problem with C.
- deleted 5y ago[deleted]
- klyrs 5y agoIf it's not wrong...
- rightbyte 5y agoThe confusing part for beginners is maybe to know were to start the spiral (or right left scheme) in a cast since there is no identifier.
- MauranKilom 5y agoThe only particularly messy part in the C code there is the (int*(*)[]) type cast. My intuition (because I don't usually have to deal with this kind of nonsense) is "cast to a pointer to an array of int pointers". cdecl confirms that: https://cdecl.org/?q=int*%28*x%29%5B%5D https://cdecl.org/?q=int*%28*x%29%5B%5D So we cast a (pointer to pointer to int) to (pointer to array of int pointers) [we can ignore the detour through void*] and then immediately dereference through all three layers. Which gives us back the only int in the program. Excellent example of how to be a Three Star Programmer I guess: https://wiki.c2.com/?ThreeStarProgrammer https://wiki.c2.com/?ThreeStarProgrammer
- pmarreck 5y agoTIL about cdecl.org I don't code in C day to day but this will help for the odd time I need to understand C code!
- pornel 5y agoOut if curiosity I've checked what c2rust.com thinks about it: **(*(p as *mut [*mut libc::c_int; 0])).as_mut_ptr() = 1 as libc::c_int; which is still needlessly complicated, and not even quite accurate due to giving the array a 0 size (the as_mut_ptr() converts the array back to a C pointer).
- Arnavion 5y ago>and not even quite accurate due to giving the array a 0 size (the as_mut_ptr() converts the array back to a C pointer). It doesn't seem inaccurate to me, more like the best choice at hand. If the C array has a known length, the Rust code has it too. Only if the C code has an array of unknown length does the Rust code use a 0-length array. Furthermore, if the C code indexes the array of unknown length, the Rust code uses .as_mut_ptr().offset(...) instead of directly indexing the array. So the fact that it represents C arrays of unknown length with Rust arrays of 0 length does not cause any problem, because the generated code is consistent. char foo(void* p) { char (*arr)[] = (char (*)[])p; return (*arr)[1]; } char bar(void* p) { char (*arr)[3] = (char (*)[3])p; return (*arr)[1]; } ... translates to: pub unsafe extern "C" fn foo(mut p: *mut libc::c_void) -> libc::c_char { let mut arr: *mut [libc::c_char; 0] = p as *mut [libc::c_char; 0]; return *(*arr).as_mut_ptr().offset(1 as libc::c_int as isize); } pub unsafe extern "C" fn bar(mut p: *mut libc::c_void) -> libc::c_char { let mut arr: *mut [libc::c_char; 3] = p as *mut [libc::c_char; 3]; return (*arr)[1 as libc::c_int as usize]; }
- Joker_vD 5y agoTL;DR: void f(int *a) { int **p = &a; **p = 1; // the same as *a = 1; } Making a concise explanation of how exactly the third dereferencing disappeared is left as a further exercise for the reader.
- tyingq 5y agoFor those mystified by certain bits of Perl, the built-in Deparse module is nice. perl -MO=Deparse /some/script Outputs a (usually) more readable equivalent, and also works for one-liners that use -e "somesnippet".
- _pmf_ 5y agoWhy is taking the address of a parameter legal? Doesn't this depend on the ABI and could be a register?
- jcranmer 5y agoThe parameter is copied into a stack variable so that you can take its address in such cases.
- ximeng 5y agohttps://stackoverflow.com/questions/34519318/c-address-of-function-parameter https://stackoverflow.com/questions/34519318/c-address-of-fu... suggests it's guaranteed by the standard to be OK. In the standard at https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf https://web.archive.org/web/20181230041359if_/http://www.ope... 6.5.3.2 Address and indirection operators Constraints 1 The operand of the unary & operator shall be either a function designator, the result of a [] or unary \* operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier. So as the parameter is an lvalue it is guaranteed to work with the & operator.
- deleted 5y ago[deleted]