Live data from Hacker News

How to design a replacement for C++

apenwarr.ca

41–50 of 75 posts

Re: How to design a replacement for C++

#42
Agree with most parts of the article, one huge issue I didn't see mentioned.

Working with C/C++ for embedded systems often means you have to have total control of the memory layout of your objects. You sometimes want to define classes which have data members that directly map on to something in memory, and you have to know exactly how the class will be layed out in memory, and more importantly, that it won't take up extra memory without your knowledge.

Specifically, his point #5, "Automatic vtable generation.", might get in the way. C++ gets away with vtables because they aren't included by default, only included when you define a virtual method. The same needs to be true of any replacement.

Re: How to design a replacement for C++

#43
tl;dr, he just wants a language that somehow magically has all the features of C++ while somehow magically not sucking at the same time. Also, regarding the first 2: fuck you too, author.

Re: How to design a replacement for C++

#44
I find his talk about a language's suitability for kernel writing to be like people buying sporty cars they will never race or off road vehicles they will never drive off road. Most people, even including embedded programmers, will never actually write (or need to write) a kernel.

Especially his comment about garbage collection: if you're not going to have garbage collection, frankly, what's the fucking point? You might as well continue to use C/C++ then.

He is demonstrably wrong that you cannot reliably combine garbage collected an non-garbage collected memory with a mark sweep collector! My C++ to Io binding library builds C++ objects in script and stores script objects in C++, and Io uses a mark-sweep collector and I am using it right freaking now this very minute in a game I'm programming without trouble. I regularly profile it in valgrind; there are no memory corruptions. The solution? Give the script-created C++ objects the same semantics as C++ code does. C++ temporaries are created in script with a "temp" keyword and get passed by value. Long lived C++ objects are created with a "new" keyword (in script) which stores a pointer to the C++ object. You have to manually call "delete" (again, in script) on the script-object-containing-the-C++-pointer to delete the objects. Here's the beauty of the logic: as long as you are holding the pointer, the script object will not be collected. If you do allow the script object to be collected, by definition that means it is unreachable, so by definition you FORGOT to call delete on it, ergo the system could then throw an exception if an undeleted C++ pointer is ever collected. So rather than a problem, it actually helps you catch undeleted memory.

For C++ objects that hold script objects, I simply force any C++ object that wants to hold a pointer to a script object to implement my IMarkable interface, and the script binding is smart enough to know that if a C++ implements IMarkable, it gets included in the mark-sweep so it can mark off its owned script objects.

Re: How to design a replacement for C++

#45

Earlier quoted context omitted.

Interesting idea, but how are you going to support "functions" like getc() that are actually macros?

Most C libraries already implement those as both functions and macros. The macro definition usually shadows the function definition, but you can access the function version if you want, e.g. in glibc, getc() will call the macro, while (getc)() will call the function. The macro is just a wrapper for a function anyway in glibc (the internal _IO_getc), so there's no real reason to retain it except for historical reasons…

> but you can access the function version if you want, e.g. in glibc, getc() will call the macro, while (getc)() will call the function.

Are you sure about this?

Re: How to design a replacement for C++

#46
1. Instead of the ability to directly call and be called by C/C++, include a really kick-ass Foreign Function Interface that can JIT the hookups and wrappers. And more important than call compatibility IMO is C++ object ABI compatibility. (I realize there is no universal C++ ABI; just pick one and be compatible with it.) If you can declare a struct or class in the language and make its layout match a C or C++ class/struct to the point of being able to pass them back and forth, that's the important feature. The other important thing is some C API's use callback functions, without passing a user object that you can use to implement a closure out of it. A language with the ability to dynamically emit new functions (built in JIT) would be able to build a new C function just for one object to receive a callback.

2. Fuck the C preprocesser. An architecture based on #include files is a joke; you will never make something better than C++ if you stick with that compilation model, you'll only make another C++ (corollary to the phrase, those who don't understand Unix are doomed to re-implement it poorly). Here's an idea: sandbox the god damn shit preprocessor and parse its output to provide input to the FFI (foreign function interface) JIT compiler. Under no circumstances allow macros defined in an #include to spill into the language proper. This is not negotiable.

3. No, mark-sweep garbage collection can be made to coexist with C++-style memory management.

4. If you aren't going to include built-in threading or some better form of concurrency, why bother? Unless you have a time machine and intend to go back to 1980 to introduce your language there.

5. I don't think it's that clear cut (about requiring (or not) a standard library) but I won't argue this point.

6. I think you're right in the sense that if you do include dynamic typing it is going to lead to creating something completely different than the stated goal. I'd say it would be a better language, but it would not be the correct language for replacing a statically typed one.

7. I think exceptions are an important language advance, but I can't dispute that they can cause a mess of problems when it comes to cross-language (or cross-thread) boundaries.

8. If you look at C, it is not too verbose for coding the types of concepts which it supports (function calls with a small number of control-flow statements). But it does not allow you to define new concepts. Of course C is terse for calling functions: those are C's atoms, it's fundamental language element! So IMO comparing to C code based on how easy it is to call a function is the wrong thing to think about. True improvements would come at the macro scale from better organization of groups of functions (e.g. into classes) or at a semantic level, by not needing to specify a function explicitly at all (e.g. operator overloading).

Re: How to design a replacement for C++

#47
Sounds like he is begging for http://ooc-lang.org/ =)

Opt-out garbage collection, type inference, operator overloading, full-blown OO but you can use covers if you just want to have methods on primitive types, some type inference, saner syntax, you can include C headers all you want (but it has a real, good module system), one-time declarations in any order, objects are by-reference....

Besides, you can replace the SDK pretty much as you want, you're not tied to any 'threads' implementation, the proof being: there's actually an OS written in ooc! http://github.com/tsion/oos

(Of course I'm barely scratching ooc's features right now)

Re: How to design a replacement for C++

#48
post #23

Earlier quoted context omitted.

import_c You can import C headers without your language being a superset of C.

Yes. Many a time I have wished to import a library's symbols without importing its macros. Or better yet, import its symbols under alternate names. "namespace x = import " anyone?

Yes, the biggest problem with #includes is the namespace cross contamination they can cause. I once tracked a build problem down to someone #defining usleep(time) to 'sleep(time/1000)' (or something similar). Problem was, usleep was included later, so the definition of the real usleep got replaced with gibberish that did math inside a function declaration.

Re: How to design a replacement for C++

#49
post #9
post #2

I have two questions. Which qualities of C++ would one want to maintain in a replacement, and what is one using the replacement for? For the second question, the article's answer is: kernels, drivers, highly performance-sensitive code like game engines, virtual machines, some kinds of networking code, and so on. And for me in particular, it also includes new plugins to existing C-based legacy systems, including Micro…

The problem with C++ isn't so much that it's compatible with C or that you shouldn't pay for any features that you shouldn't use -- it's that they used the same dumb linker as C and so most advanced features are grossly limited and ended up as a twisty maze of text manipulation and includes.

Absolutely! Damn language innovators who think that solving module linkage is "not sexy" but modifying syntax to be unprocessable by people and machines is.

Google Go is fast because it stole ideas from Wirth and followers (Turbo Pascal, Modula) who actually took the care to provide sane module linkage for decades. The proper path to the evolution of C was to keep includes and classic linking but to allow "module linking" too. All that was being done for decades, only not in C and C++.

Re: How to design a replacement for C++

#50
Some comments on your proposed additions (my other comments were about the proposed reductions):

- I don't think .NET-style generics can be implemented in a statically typed language that doesn't have a common base class (e.g. everything derives from class Object). This is an oversimplification to say this but at some level generics are implemented as syntatic sugar for typecasts between Object and the type parameter type. If you want generics instead of templates, a lot of C#'s design gets pulled in along for the ride. Nothing wrong with that (C# is a fine language), just as long as you understand it will happen and why. But also be aware that it means you will never be able to make a generic array class that performs as fast as a C++ STL vector does - which violates your own starting criteria, that it has to be as fast as C++ to succeed.

- Most of the time I wish pass-by-reference were implicit, but pass by value has some special consequences in C++ that make it unlike many other languages. I was able to write a class that does fast bit-twiddling on itself, with its internal representation being an integer, and the entire thing was no more expensive in memory or speed than C functions bit twiddling on an int. So I was able to get abstraction and raw speed with no compromises. That's a rare use case, but I think it is illustrative of why pass by value is in C++ - you don't need it most of the time, but when you need it, you really need it.

As for not being able to reseat a reference, I found that frustating too until I started learning LLVM and about SSA (static single assignment). The common answer to why you can't reseat a C++ reference is just that it has something to do with compiler optimizations. That common answer is too weak; it has EVERYTHING to do with how the compiler optimizes the code. Basically, if you're asking why can't I reseat a reference, you don't understand what a reference is. But it's not your fault: what you really don't understand is that C++ uses the word reference to refer to a thing that is not exactly like references as used in other languages. I used to think a reference was syntactic sugar for a pointer; that's not quite true. A C++ reference literally IS the object; it's just converted to a pointer during the process of a function call. The reason you can't reseat the reference is that if you could the static analysis of the program becomes immeasurably harder and later in the code the compiler cannot be absolutely sure that it knows what the reference really refers too.

In a way, not being able to reseat a C++ reference is a lot like not being able to assign to a constant defined by a macro or not being able to assign a new address to a function at runtime. It's part of a class of things that happen at a stage before runtime, and to change the system so that it would be possible would require introducing a level of indirection.

Post reply on HN