Live data from Hacker News

Cling: Running C++ in an interpreter

blog.coldflake.com

41–49 of 49 posts

Re: Cling: Running C++ in an interpreter

#41
post #35
post #23

To provide more precedents and a little history: The first C "interpreters" I know of were for Lisp machines: Symbolics' C compiler ( http://www.bitsavers.org/pdf/symbolics/software/genera_8/Use... ) and Scott Burson's (hn user ScottBurson) ZetaC for TI Explorers/LMIs and Symbolics 3600s (now available under the public domain: http://www.bitsavers.org/bits/TI/Explorer/zeta-c/ ). Neither of them are interpreters, just…

The only problem is how to deal with casting arrays of integers (or whatever) to arrays of bytes. But that's a problem for portable C software anyway. Byte-wise access to objects is legal and completely portable between conforming implementations. What isn't portable are arbitrary type conversions through pointer casts as these violate the effective typing rules. Such casts may break in practice due to mis-alignment…

> Byte-wise access to objects is legal and completely portable between conforming implementations.

That makes absolutely no sense. Just think about endianness for example. Type conversions in C are extremely tricky, and in many cases are not guaranteed to be portable across different compilers even on the same architecture. There is a good explanation of what you can and cannot count on in chapter 6 of Harbison and Steele's C A Reference Manual.

Re: Cling: Running C++ in an interpreter

#42
post #29

Earlier quoted context omitted.

Emscripten in no way emulates LLVM (as far as I know it's the only C-to-JS compiler that uses LLVM, and hence must be what you're referring to!) — it compiles LLVM to JS, with no emulation, and there's currently work going on to re-implement it as a LLVM backend (it currently is an entirely separate codebase that takes LLVM bitcode in and spits JS out).

My mistake, I thought Emscripten was an LLVM VM in JS. I'll take a look at it to see what the output actually does.

Looking at issues in https://github.com/kripken/emscripten/wiki/Filesystem-Guide it seems to be there would be a big win in having an LLVM VM - you'd be able to have synchronous IO and have threads.

Re: Cling: Running C++ in an interpreter

#43
post #30
post #23

To provide more precedents and a little history: The first C "interpreters" I know of were for Lisp machines: Symbolics' C compiler ( http://www.bitsavers.org/pdf/symbolics/software/genera_8/Use... ) and Scott Burson's (hn user ScottBurson) ZetaC for TI Explorers/LMIs and Symbolics 3600s (now available under the public domain: http://www.bitsavers.org/bits/TI/Explorer/zeta-c/ ). Neither of them are interpreters, just…

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…

> 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.

Re: Cling: Running C++ in an interpreter

#44
post #36
post #19

Earlier quoted context omitted.

> C++ compilation speed (and thus interpretation speed) will make it quite impossible to use it as an extension language. I think you vastly underestimate the speed of the clang C++ compiler (or any modern C++ compiler at that). I can't imagine that the compilation time for anything that could be classified as an "extension" would be significant in any meaningful way.

clang doesn't outperform gcc on most projects I work on or that speed increase is not significant. I run quite a lot of test-suites - maybe I should try to measure them and provide some real numbers for reference instead of hand-wave myself through this argument. If I where to provide extensions points to my C++ project they would certainly contain templates and that would also mean that they would require significan…

> I'm inclined to believe you if we are talking about a Qt-style C++, but that is only a subset of possible code. I would be happy to be proven wrong, too.

If the alternative is to use a separate language altogether to implement extensions, then you are already limiting yourself to a subset of all possible (in C++) code, so what's wrong with doing that in C++ instead?

That being said, most of your problems can be solved with header optimization, we had gotten a full-rebuild of our multimillion line code-base down to about 2 minutes. So if an extension is only a file or two I'm sure you could manage very reasonable compile times, esp with optimization turned off.

> Even if I didn't, a single header that pulls in a huge preprocessor library could ruin speed.

In the case that you really need that header (If you are using as an extension language, it should be designed so you don't!) precompilation of certain units can greatly improve speed. clang in particular has very impressive precompilation support.

Re: Cling: Running C++ in an interpreter

#45
post #37

Earlier quoted context omitted.

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.

Re: Cling: Running C++ in an interpreter

#46
post #45

Earlier quoted context omitted.

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.

Re: Cling: Running C++ in an interpreter

#47
post #45

Earlier quoted context omitted.

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

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...

Re: Cling: Running C++ in an interpreter

#48

Earlier quoted context omitted.

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…

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 initializat…

Before I give you an example, I will define what the term undefined behavior means by quoting the standard - Section §1.3.12 (of the C standard, not the C++ standard which is horribly ginormous and hard to read):

    behaviour, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements 3.

    Undefined behaviour may also be expected when this International Standard omits the description of any explicit definition of behavior.
The reason undefined behavior is dangerous is that the standard does not guarantee any particular behavior and the implementation is free to do whatever it wants - ignore it, give an error message, delete everything on your hard drive.. whatever.

The two most commonly cited piece of undefined behavior is modifying a variable twice in one sequence point. The standard says:

    Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression.
This code snippet invokes undefined behavior:

    a = b++ * ++b;
because b is modified twice within one sequence point. More information here http://stackoverflow.com/questions/4176328/undefined-behavio...

-----

For more real world examples of where undefined behavior may bite you in the ass in C++, take a look at Washu's simple C++ quiz. It's only four questions: http://www.scapecode.com/2011/05/a-simple-c-quiz/

Take a moment to answer the questions before looking at the answers.

Once you've done that, here are three more quizzes by the same guy - these ones are about OOP in C++, so may be much more relevant to your question: http://www.scapecode.com/2011/05/c-quiz-2/ and http://www.scapecode.com/2011/05/c-quiz-3/ and http://www.scapecode.com/2011/05/c-quiz-4/

Re: Cling: Running C++ in an interpreter

#49
post #41
post #35

Earlier quoted context omitted.

The only problem is how to deal with casting arrays of integers (or whatever) to arrays of bytes. But that's a problem for portable C software anyway. Byte-wise access to objects is legal and completely portable between conforming implementations. What isn't portable are arbitrary type conversions through pointer casts as these violate the effective typing rules. Such casts may break in practice due to mis-alignment…

> Byte-wise access to objects is legal and completely portable between conforming implementations. That makes absolutely no sense. Just think about endianness for example. Type conversions in C are extremely tricky, and in many cases are not guaranteed to be portable across different compilers even on the same architecture. There is a good explanation of what you can and cannot count on in chapter 6 of Harbison and S…

There's also a perfectly good list of what you can and cannot count on in the ISO C standard, no need for secondary literature.

While the values of the bytes are not specified, the ability to get at them is, and a conforming implementation needs to provide this ability.

Post reply on HN