
Cello – High Level C - thisisastopsign
http://libcello.org/
======
kazinator
The strategy in the GC for determining the stack top for hunting GC roots will
not work on all architectures.

On aaarch-64, the address of a local dummy variable may be above a register
save area in the stack frame, and thus the scan will miss some GC roots.

In TXR Lisp, I used to use a hacked constant on aarch64:
STACK_TOP_EXTRA_WORDS. It wasn't large enough to straddle the area, and so
operation on aarch64 was unreliable.

[http://www.kylheku.com/cgit/txr/commit/?id=3aa731546c4691fac...](http://www.kylheku.com/cgit/txr/commit/?id=3aa731546c4691fac333846f15950578425f43da)

A good stack-top-getting trick occurred to me: call alloca for a small amount
of memory and use _that_ address. It has to be below everything; alloca cannot
start allocating above some register save area in the frame, because then it
would collide with it; alloca has not know the real stack top and work from
there.

Since we need to scan registers, we use _alloca_ for the size of the register
file (e.g. setjmp jmp_buf), and put that there: kill two birds with one stone.

[http://www.kylheku.com/cgit/txr/commit/?id=7d5f0b7e3613f8e8b...](http://www.kylheku.com/cgit/txr/commit/?id=7d5f0b7e3613f8e8be84ac0d541a7bbcb4782f1d)

~~~
naasking
> On aaarch-64, the address of a local dummy variable may be above a register
> save area in the stack frame

Then use two stack frames! Every problem can be solved by adding an additional
level of indirection. ;-)

~~~
kazinator
In this case it won't help, because:

0\. We are already in a frame that doesn't take any arguments of the "val"
object type; how come that's not good enough?

1\. The current stack frame is entered with a bunch of callee-saved registers,
some of which contain GC roots.

2\. The current stack frame's code saves _some_ of them: those ones that it
clobbers locally. It leaves others in their original registers.

3\. Thus, if a another stack frame is called, there are still some callee-
saved registers, probably containing GC roots, and some of these will go into
the area below the locals.

4\. You might think that if the save all the necessary registers ourselves
into the stack and _then_ make another stack frame, we would be okay. But in
fact, no. Because by the time we save registers, the compiler generated
function entry has already executed and saved some of those registers into the
below-locals save area and clobbered them for its own use! So our snapshot
possibly misses GC roots. The compiler generated code always has "first dibs"
at the incoming registers, to push them into the below-locals save area, thus
kicking the GC roots farther up the stack.

------
aerovistae
Seen this posted here years ago. Now as then, my gut feeling is that anyone
doing serious work in C would never use something like this-- I feel like the
fine grained low level control is exactly the reason they chose C in the first
place, and they're not looking to escape from it or they would just choose a
different language.

~~~
flukus
> I feel like the fine grained low level control is exactly the reason they
> chose C in the first place

That's not the only reason, there is also simplicity, static typing and
performance. If you favor the later two for whatever reason it can be used in
places where you'd normal write a python/shell script or small program without
too much extra effort (see
[https://github.com/RhysU/c99sh](https://github.com/RhysU/c99sh) or suckless
tools). Complexity is where Cello seems to fall down though, it seems like it
introduces much more complexity than just using plain C with a decent
"standard" library like glib.

~~~
da_chicken
> That's not the only reason, there is also simplicity, static typing and
> performance.

I think the only meaningful benefit here is performance.

Simplicity is at best determined by the nature of the problem and at worst a
completely subjective opinion for C.

Similarly, static typing is not usually something the programmer should care
about that much. You need to know which paradigm your language uses, of
course, but beyond that it does not matter all that much. IMX, you're more
concerned with type safety, and C is not fully type safe like, say, Java is.

~~~
flukus
Yes it's somewhat subjective, but other languages for the use case usually
contain a lot more abstractions. They're more complex languages but they might
enable less complex solutions to the problem at hand.

> IMX, you're more concerned with type safety, and C is not fully type safe
> like, say, Java is.

I'm concerned with finding errors, preferably at compile time. There have been
very few times being fully type safe runtime like the jvm have done much for
me compared to the java compiler. If I wanted more down that road then rust or
ada would probably be better.

------
analog31
This is just an amusing aside: C is the lowest level of an actual cello. ;-)

~~~
aneutron
I would have never gotten the wordplay at hand. Thanks ! Very clever.

------
drongoking
Isn't this sort of what Glib is getting at? Bringing higher level data
structures and capabilities (extendable arrays, hash tables, heaps, etc.) into
C.

[https://developer.gnome.org/glib/stable/glib-data-
types.html](https://developer.gnome.org/glib/stable/glib-data-types.html)

You don't get Cello's macros, and it uses reference counting instead of
invisible garbage collection, but you get a lot of fun high-level
capabilities.

~~~
jopsen
And if you really want to go high-level wouldn't Vala be the language?

~~~
war1025
Vala is actually a really pleasant way to interact with the various bits of
the Gnome environment.

It's a shame, in my opinion, that it never really received widespread love.

I think all serious work on it stopped about a decade ago.

~~~
jopsen
Isn't it still used by gnome, elementary, etc?

I suppose it's very gnome specific.

~~~
war1025
It's used but some of the core gnome developers have advocated for people to
stop using it.

What I can find with a quick search this morning: [1] [2]

Then again, there are also several people who still say it's worth using: [3]
[4]

So maybe it's just Emmanuele Bassi advocating against it. If you look at the
development pace, though, it hasn't really seen any significant development
for quite a long time.

[1]
[https://twitter.com/ebassi/status/827482509982195712](https://twitter.com/ebassi/status/827482509982195712)

[2] [https://www.bassi.io/articles/2017/02/13/on-
vala/](https://www.bassi.io/articles/2017/02/13/on-vala/)

[3] [https://blogs.gnome.org/mcatanzaro/2017/02/19/on-problems-
wi...](https://blogs.gnome.org/mcatanzaro/2017/02/19/on-problems-with-vala/)

[4] [https://blogs.gnome.org/despinosa/2017/02/14/vala-is-not-
a-p...](https://blogs.gnome.org/despinosa/2017/02/14/vala-is-not-a-
programming-language/)

------
winrid
There was a snippet I saw a while ago where someone made C look like Java for
a joke, using macros. I wish I could find it to share here, it's great.

~~~
mar77i
The original bourne shell source was using mac.h (
[https://minnie.tuhs.org/cgi-
bin/utree.pl?file=V7/usr/src/cmd...](https://minnie.tuhs.org/cgi-
bin/utree.pl?file=V7/usr/src/cmd/sh/mac.h) ), a bunch of macros, according to
wikipedia, "to give the C source code an ALGOL 68 flavor."

Here's the original source tree, if you're interested:
[https://minnie.tuhs.org/cgi-
bin/utree.pl?file=V7/usr/src/cmd...](https://minnie.tuhs.org/cgi-
bin/utree.pl?file=V7/usr/src/cmd/sh)

------
gok
Previously
[https://news.ycombinator.com/item?id=14091630](https://news.ycombinator.com/item?id=14091630)

~~~
dang
Also 2015:
[https://news.ycombinator.com/item?id=10526159](https://news.ycombinator.com/item?id=10526159)

2014:
[https://news.ycombinator.com/item?id=8799070](https://news.ycombinator.com/item?id=8799070)

2013:
[https://news.ycombinator.com/item?id=6047576](https://news.ycombinator.com/item?id=6047576)

------
kazinator
I'm left wondering about the iteration example that is also quoted in the home
page:

[https://github.com/orangeduck/Cello/blob/master/examples/ite...](https://github.com/orangeduck/Cello/blob/master/examples/iteration.c)

Okay, so the vector is garbage-collectable once the function terminates ...
but it has references to stack-allocated integers i0, i1 and i2. That leaves
me wondering: won't the GC walk these and trample on stack memory that has
been deallocated/reused.

(Maybe those integer values have a tag right in the _val_ pointer that gets
the GC to avoid dereferencing them.)

------
noncoml
Just my opinion, don't mean to be inflammatory, but if the user has to know
and manually manage stack vs heap objects, then I wouldn't call it "High
Level" language.

~~~
zozbot234
That raises a question, would you call C# a "High Level" language?

~~~
rubber_duck
In C# it's more relevant to understand semantics (ref/value type) than
allocation details (unless you actually care about low level details for
performance/interop)

------
rs23296008n1
Didn't C++ start out as a set of hacks on C? Fairly sure it was originally a
preprocess stage ahead of an ordinary c compiler.

Raises the question of how usefully far you can make C twist using macros /
preprocessor.

Candidates like Forth or Lisp seem possible. A few weekends at most. Might
need to take a few liberties.

Python... Perhaps if you implement a less dynamic subset? Duck typing may trip
you up. To what extent?

What about Elixir?

~~~
DonaldFisk
I wrote my own Lisp. The virtual machine, which is a stack machine, is written
in C. The interpreter, which runs until the system compiles itself, is written
in C, but makes heavy use of C macros. The rest of the code, including the
compiler, is in Lisp.

Code in the interpreter is directly converted to byte code, e.g. the macro Car
generates the virtual machine instruction Car, rather than executing the code
for car. The alternative would have been to generate byte code by hand, would
have been error-prone. Here's the code for cons and let:

    
    
        Define("cons", 2)
          Local1 Local2 Cons Ret
        Termin
    
        DefineF("let")
          Local1 Car
          Local2 /* initialize new env */
          Prog(1)
          Ret
    
          Params(2)
          Until Local1 Null Do
            Local1 Caar /* var */
            Local1 Cadar Free12 Call("eval") /* val in old env */
            Local2 /* env */
            ACons
            SetLocal2 Pop /* update new env */
            PopLocal1
          Od
          Free11 Cdr Local2 Call("progn") /* use new env */
          Ret
        Termin
    

It is actually C, with heavy use of macros. But it can be read as Reverse
Polish Lisp. It can also be thought of as a Lispy Forth.

~~~
rs23296008n1
Interesting approach. maybe develop it further into a JIT arrangement or a
library builder.

Brainf*ck is another classic.

------
scoutt
Thanks. I didn't know about this library. Interesting, but perhaps I am
missing something... about stack "allocation":

    
    
      var i0 = $(Int, 5);
    

vs

    
    
      int i0[5];
    

In both cases it doesn't need GC. What would be the reasons for redefining it?
I wonder how it couples with local _static_ variables.

------
shmerl
Is it using macros to achieve that?

------
self_awareness
For a different take for "better C", try Zig language, it looks pretty cool.

[https://ziglang.org/](https://ziglang.org/)

------
loeg
This gets reposted every couple years and it's still bad for all of the same
reasons.

It's not higher level than C in the sense that you get any additional safety
guarantees or real beneficial abstractions. If you are fine without the safety
but want abstractions, use C++. If you want safety and abstractions, use Rust
or Go or Zig. If you really want a transpile-to-C language, you've got Nim.

Finally, it's not good at being C; everything it does is poor practice and
should be quickly recognized as such by experienced C developers, IMO. It's
got no developer community and no real-world production consumers.

------
h0bzii
What type of sorcery is this?

~~~
keyle
The good one, dark, very dark.

~~~
pmiller2
Yeah, the foreach macro is particularly interesting to me in that respect.
Quite a neat bit of sorcery.

------
FpUser
This is great. It made me smile.

------
tuczi
Why not just C++?

~~~
macintux
Speaking for myself, I’ve never found C++’s complexity appealing, and it only
seems to be getting worse over the last 20 years.

As the creator says, it’s not for production use. If I’m doing a side project,
I’d give this a serious look.

~~~
zozbot234
> Speaking for myself, I’ve never found C++’s complexity appealing, and it
> only seems to be getting worse over the last 20 years.

True, but that's why we've got Rust these days. (Rust is actually _more_
optimized than C, e.g. it will automatically reshuffle your structs to get rid
of excess padding, and reference accesses will automatically take advantage of
compiler-checked 'restrict' constraints, thus equalizing performance with e.g.
FORTRAN.)

~~~
throwaway17_17
How is Rust an answer to not liking C++‘a complexity. With the complexities
inherent in the borrow semantics (with the box ref cell stuff that comes with
it) and the complexities inherent in the type system and trait system, Rudy
seems to be at least as complex as C++. In fact this point gets tossed out a
lot, Rust is not a C replacement for C developers, it is at best a C++
replacement for developers who want something more complex than C. Not
commenting on the quality of the complexity, just that it seems odd that you
would say Rust is an answer to disliking complexity.

~~~
GolDDranks
But note what those complexities buy you: they bring restrictions and
limitations that allow you reason _more_ about the code.

This is in stark contrast to some other complex features that allow more stuff
to happen with less code.

Rust is indeed, complex in the sense that it's features are non-trivial, but I
find it less complex than C++ in the sense that it has 1) less surface syntax
(because of lack of historical baggage) 2) more cohesive, principled feature
set (again, hindsight is 20/20) 3) it's inherently more limiting, which helps
reading, understanding and reasoning about code.

------
einpoklum
This isn't a library, it's a sort-of-a-modification of C, it seems.

Well, for a non-C language with high-level abstractions that lets me use C
code relatively seamlessly - I'm content with C++. Many complain about its
complexity, but you can actually avoid a lot of that complexity in _your_ code
using facilities with complex implementation but relatively easy use.

------
reanimus
Looks interesting, but I can't help but notice they're distributing their
source tarball via that site, and it doesn't have HTTPS. I don't understand
why projects don't have SSL certs these days, especially considering Let's
Encrypt has automated it all and made it free.

