Hacker News new | ask | show | jobs
by mahmud 5051 days ago
That Oleg link has me nerd-snipped.

What I can't wrap my head around is how one would implement pointer-arithmetic with these closures? C pointers are not just references to cells, but those cells are guaranteed to be contiguous (up to a certain limit, be it an VM allocation unit, say "page", or all available system unit in VM-less systems)

That is to say, C pointers are not like ML references. Along with SET and REF they also allow addition, subtraction, scaling, etc.

For closures to model C pointers, wouldn't they need to order the allocation of cells in some manner? say, big array? And if so, this could get expensive very quickly (worst-case being "modeling" of entire memory, i.e. emulation) without certifying compiler or at least some exhaustive pointer analysis.

Hope I'm wrong on this.

2 comments

> What I can't wrap my head around is how one would implement pointer-arithmetic with these closures?

  (defstruct memptr
    mem
    (ptr 0))

  (defun allocate-memory (size) ;; shared by malloc and static allocation
    (make-memptr :mem (make-array size :adjustable t :initial-element 0)))

  (defstruct place-ptr
    closure)

  (defmacro vacietis.c:mkptr& (place) ;; need to deal w/function pointers
    (let ((new-value (gensym)))
      `(make-place-ptr :closure (lambda (&optional ,new-value)
                                  (if ,new-value
                                      (setf ,place ,new-value)
                                      ,place)))))

  (defun vacietis.c:deref* (ptr)
    (etypecase ptr
      (memptr (aref (memptr-mem ptr) (memptr-ptr ptr)))
      (place-ptr (funcall (place-ptr-closure ptr)))))

  (defun (setf vacietis.c:deref*) (new-value ptr)
    (etypecase ptr
      (memptr (setf (aref (memptr-mem ptr) (memptr-ptr ptr)) new-value))
      (plate-ptr (funcall (place-ptr-closure ptr) new-value))))

  (defmethod vacietis.c:+ ((x number) (y number))
    (+ x y))

  (defmethod vacietis.c:+ ((ptr memptr) (x integer))
    (make-memptr :mem (memptr-mem ptr) :ptr (+ x (memptr-ptr ptr))))

  (defmethod vacietis.c:- ((ptr1 memptr) (ptr2 memptr))
    (assert (eq (memptr-mem ptr1) (memptr-mem ptr2)) ()
            "Trying to subtract pointers from two different memory segments")
    (make-memptr :mem (memptr-mem ptr1) :ptr (- (memptr-ptr ptr1) (memptr-ptr ptr2))))
As you can see I don't do anything with type declarations, so arithmetic performance is going to be terrible. Multiple levels of pointer indirection work correctly with pointer arithmetic, since all pointers which can be legally added/subtracted are first-class objects.
The C standard basically only guarantees that pointer arithmetic works when the pointers involved all point to the same array object (it also allows for a pointer just off the end of an array object). Other pointer arithmetic or comparison is undefined behavior and an implementation can do whatever it pleases.
So I implemented this stricter definition of C pointers and it's neither interesting, nor representative of Real World uses that I know of. Need to investigate a bit more.
stricter definition of C pointers - this made me laugh, as the less-strict definition of C pointers is called undefined behavior.

This leads me to this statement based on what you said: interesting Real World C programs make use of undefined behavior

> interesting Real World C programs make use of undefined behavior

I assume you've worked with a significant amount of real world C programs? Because they surprisingly often do. The difficulty with porting many programs to 64-bit for instance, is due to relying on implementation defined behavior.

I assume you've worked with a significant amount of real world C programs?

Only embedded systems (AVR & PIC24). I have much more experience in C++, which I've used for both Desktop apps and telco server components.

The beauty of C (over C++) is that the standard is actually readable. C++ especially is a quagmire of undefined behavior. The scary thing about C/C++ is that its easy to hit undefined (or, at least, as you state, implementation defined) behavior and not even realize. Often the code looks valid, does what it looks like it does, yet is actually undefined or implementation defined and will break elsewhere.

With that said, while I don't expect everyone to have memorized the standard, I do hope most would have at least enough familiarity to avoid most cases of undefined behavior.

Most of what I learned about C++ I got from books, blogs and some great C++ guys (mostly from the boost community). I have to admit I did not read the standard at all.

could you give an example of a case where you hit undefined behavior since I hardly seem to recall a case where that bit me in the past? (I'm mostly working on embedded systems (PPC & ARM))

The cases that come to my mind for C++ all involve initialization...