That's like saying "Why not just use lisp?" to a python programmer. They're entirely separate languages (albeit it they do share some commonalities, but not as many as you might think).
C is my main language and I dabble in C++. I really dislike C++. I love C. I welcome any efforts to add a bit of higher level functionality to C. I have no desire (ever) to switch to C++.
C programmer here. Cello isn't really C. So as long as you're using something that isn't quite C, and you want an object model, I think "why not C++?" is a reasonable question.
> That's like saying "Why not just use lisp?" to a python programmer. They're entirely separate languages
That's not really true now is it? Sure, C++ and C are separate languages but they have a common history and everyone knows that. One can write extensive programs that are valid for both C++ and C compilers. C isn't a true subset of C++ but for from many practical perspectives it is. In practice they have a common ancestor language that still can be (more or less) compiled with both compilers.
Lisp and Python however... they have no such common ancestor and the syntax are very different.
For "pure C" with high level string and dynamic memory handling (with data types in both heap and stack), consider checking https://github.com/faragon/libsrt (not production ready for "NASA-like" things, but I've put an important effort in test-coverage, aliasing awareness, etc.) :-)
I'm not the parent, but C has a certain simple elegance: everything's an int; those things which aren't ints (e.g. compound structures like structs and arrays) are represented using pointers and offsets, so in a sense they're also ints; except for floats, but meh.
In C++, everything is an int; except for objects; and templates; and lambdas; and classes; and exceptions; and all the other things which keep getting chucked on to the pile.
I like languages with a clear, simple concept behind their operation: lambda calculus, combinatory logic, Scheme, Forth, Joy, Pure, etc. C is a bit gnarly, but passable. C++ is horrible.
Yeah, that phrase "everything's an int" scares me (and I'm a C programmer---been programming in it since 1990). At one time I was playing around with Viola (http://www.viola.org/) (quite possibly the first graphical web browser). The code is a mess precisely because it grasps the "everything's an int" and doesn't let go. Once you get it to compile (and out of the box it doesn't any more) it barely runs on a 32-bit system (which it assumes to an nth degree---sizeof(int) == sizeof(long) == sizeof(void *)) and promptly crashes on a 64-bit system.
Horrible, horrible code.
You can write clean C without casts and treating everything as an integer, but you have to start your code with that goal.
As a C programmer, I would also agree. "Everything is an int" is not a good way to be thinking about things. There's a reason that `intptr_t` exists. Well written C shouldn't rely on any integers being specific sizes or the same size unless you're using the standard types for that purpose (Llke `uint8_t`, `uint32_t`, etc.). Even then, you probably don't need them in a lot of cases, and you should basically never be casting integers into pointers unless you're using the above `intptr_t`, or `uintptr_t`. Even then, you're probably still better off just making your life simple and use a `union`.
That said, I would argue that minimal explicit casts is a pretty good goal for any C programs - I find that crazy casting tends to be a sign something could be done better. But obviously, casts are definitely necessary in some instances, so it's not like "castless" code is guaranteed to be the right way to do things anyway.
To clarify, I should probably have said "integer" to avoid confusion with the C type called "int", or to be more accurate ℤ/w for a variety of widths w.
I didn't mean "C is nice because I can just write 'int' for all the types and it works", I meant "C is nice because it represents data in a very conceptually uniform way: either as integer, or 'pointers' which are themselves integers."
The current world population is an integer, my bank account number is an integer, but that doesn't make it's meaningful to add them together. Values can have the same representation without being the same type :)
> "Everything is an int" is not a good way to be thinking about things. There's a reason that `intptr_t` exists.
intptr_t
integer type capable of holding a pointer
They're integers :)
> Well written C shouldn't rely on any integers being specific sizes or the same size unless you're using the standard types for that purpose (Llke `uint8_t`, `uint32_t`, etc.).
I never said it should; but from that same cppreference site:
int8_t, int16_t, int32_t, int64_t
signed integer type with width of exactly 8, 16, 32 and 64 bits respectively
These are also integers :)
> you should basically never be casting integers into pointers
Again, I never said you should. I didn't say, or mean, anything about casts.
I don't understand this. Floating point values aren't ints. I suppose strings are integers because they're made up of 8-bit chars (which are basically ints), but I don't understand how that is advantageous or helpful.
He was trying to illustrate the concept that in C you deal directly with blocks of memory. It can be useful because instead of worrying about the implementation of the language which you're using, you can think about what the hardware is doing and understand what's going to happen based on that.
So, for example, you know if at offset 0xDEADBEEF there is are 8 bytes which are holding a number that you want to use, you can access that chunk of memory and use it however you want. And you can interpret it how it is appropriate for your use case, for example reading it into a string, or an int.
Being grounded in hardware makes a lot of other stuff quite arbitrary, and it can become a question of style rather than functionality. And by imagining what the hardware is doing you can understand what code is doing that you have never seen before at the lowest levels (all the way to what the registers, FPU, APU, etc on the CPU are doing). That can also help with debugging.
> So, for example, you know if at offset 0xDEADBEEF there is are 8 bytes which are holding a number that you want to use, you can access that chunk of memory and use it however you want. And you can interpret it how it is appropriate for your use case, for example reading it into a string, or an int.
Except these days with strict aliasing that's not true. If you access memory through a pointer of the wrong type, you've probably committed the crime of undefined behavior, for which the compiler might punish you by breaking your code - but probably won't, leaving the bug to be triggered in the future by some random code change or compiler upgrade that creates an optimization opportunity.
Admittedly there are ways to avoid undefined behavior, but still. C gives the compiler a lot of leeway to mess around with your code.
As I said, C is gnarly, mostly for legacy and "practicality" (WorseIsBetter) reasons.
Undefined behaviour, declared as in a language standard, isn't too bad; it might forbid some potentially-useful combinations of expressions, but there'll usually be a workaround.
The problem is compilers which allow undefined behaviour by default, rather than aborting with a helpful error message. This puts the burden of avoiding undefined behaviour on the programmer, which is really bad. This might be unsolvable for C, due to the nature of its particular undefined behaviour.
For brand new languages, I'd recommend minimising undefined behaviour as much as possible, but definitely ensure that any that remains can be found statically!
As someone who likes C, I'd like to add a bit of a different look:
The aspect that I love about C is how easy it becomes to read. C offers a (arguably) nice programming interface while not allowing the programmer to hide very many things that are going on. C allows you to make nice abstractions, but those abstractions are still built upon the same basic concepts that C supports, and these abstractions generally don't fundamentally change how the language works, so you can very easily tell the control flow of your program if you simply understand the interactions between the basic parts in use.
Now that said, I'll be the first to say that I think C could benefit from a variety of different features (Some from C++, some from others, and some unique to C), and I could easily give you a fairly long "wish-list" I have for C. But, the clear nature of C is a huge advantage that I think is underrated, and is something that you really can't get back in a language once you lose it.
When you (or I) read a piece of C++ code, there are a lot more details you have to take into account then when you read a piece of C code, simply because C would force the writer to be more explicit in what they are doing in those cases.
Again though, I'll make it clear that I don't think C is the be-all end-all of languages - in particular I'm excited to see how Rust goes - but I do think it has traits that are very nice but largely ignored in most of the current languages in favor of adding lots of different ways to write your code.
C++ is very complicated. C is basically the simplest possible programming language, and the closest to the API actually presented by the processor (i.e., assembly language)
"Simple" was a poor choice of words as it doesn't capture what I mean.
I meant "fewest possible abstractions over the underlying hardware".
BF and Lisp are both much simpler than C, but don't fit what I was trying to get at, as they present a totally different abstraction that is actually quite different from the underlying hardware.
(Actually, so is C, since assembly language is itself an abstraction and processors work much differently than they did in the 70s and 80s... but as a programmer, and not a hardware engineer, I consider asm to be the baseline, zero-abstraction level ;) )
It would be hard for it to be a completely strict superset of C because that would require not introducing new reserved words (like "class") since those end up being legal variable names in C but not C++.
For people who aren't professionally familiar with C or C++ it's an easy mistake to make considering how often things refer to "C/C++" like they are a single skill.
Once upon a time there was this thing called 'cfront'. Cfront took C++ and turned it into C which you then fed through your compiler.
At that time mixing C and C++ was trivial, especially because C++ was still quite simple and the C compiler was the target.
But after that things got more complicated. C evolved, several times in fact since that time and the C++ standard evolved as well. Leading to the impression that C and C++ are merely the old and the improved version of C but it is probably much better to think of them as two distinct species that share a common ancestor, where one of the two had some very radical mutations.
It would be relatively easy for C++ to add some kinds of C compatibility, and they just haven't bothered to. (E.g., the "static" keyword for parameter array sizes.) It's a little annoying for shared headers that have to be used in C and C++ programs.
But the standards have made some effort at avoiding gratuitous incompatibility, and have even introduced changes to reflect changes in the other standard.
Also, much C code is still valid C++. Sure, you can write code that isn't, but I would guess (pulls a number out of nowhere) that 90% of the valid C code is also valid C++.
That makes them languages that have separate standards, but not completely independent standards, that share a whole lot of source code. That's something less than fully separate in my book.
Besides the various "bad old programming idioms" that do not compile under C++, there are a few genuinely useful features that have only just been added to C++ more than 15 years after they appeared in C, like hexadecimal floating-point literals (in practice, many compilers supported these, but they were not formally part of the language). Designated struct initializers are another C feature that I would love to have in C++.
This is a great example of how many of the things in C that don't compile in C++ are horrible programming practices, and it's really nice that C++ doesn't allow such garbage.
Actually those are very bad examples. In practice, compilers will produce warnings in all of those examples.
Better examples (of actually useful things) would be things like designated initializers, struct literals, declaring array lengths in args using static, etc.
I had that same problem. I wrote a C library [1] that has a structure field named "class". It's "class" because the protocol being described (DNS) calls that particular field "class" [2]. And I'm not about to pay lip service to an abomination like C++ [3][4].
Cello and C++ are have very different design goals. Cello is essentially a dynamic language implementation on top of C, and uses runtime reflection in places where a C++ programmer would probably prefer compile-time template hackery.
Idiomatic C++ is not like Cello at all. But it still allows you to do all things that Cello does - and most of them wouldn't be macros then, but classes and overloaded operators on them.
Somehow I doubt C++'s built-in object system will allow you to create classes (yes, classes, not objects) and/or alter their vtables at runtime. (I know, vtables are an implementation detail. You still get the idea.)
Not anymore so than the C one - it's just as static.
The point is that building blocks that you get from C++ are higher-level even so - classes, operator overloading, template metaprogramming etc - which should make it possible to implement something like Cello with a lot less effort (and hacks).
A good and valid reason would be compiler availability. C++11, 14 and 17 might not stable for every compiler out there.
C++11 and further can be fancy, and not be supported in every possible platform like exotic ones.
Bad reasons would be to talk about the gimmicks of C++, but generally it boils down to programmers being taught well about the language, because it's not an easy one to understand fully, as there many weird pitfalls.
C++ still has to fix some its flaws, like compile times (this should be improved with modules).
So to put it simply, you can still do good work with C because C compilers are just simple and straightforward. When you don't need C anymore (because you only use for it's size and speed, not for the high level), you use something more high level like python. C++ should allow programmers to do everything, but C++ is not perfect and not fully accessible to everyone.
Compile times aren't that slow, and the language is barely less complex than this insane hack. In fact C++'s complexity allows libraries to make their use a lot simpler. Consider string concatenation in C++ and C. Which is really simpler?
The main reason I can think not to use C++ is that C has a simple stable ABI so you can easily use C libraries from many languages and compilers. But you can always wrap C++ libraries in a C API so that's not a huge concern. Just a pain.
I beg to differ. C++ encourages placing more code than strictly necessary into header files. Even the standard headers such as <algorithm> add considerably to the compile time, and they keep getting larger with every revision of the standard. When you are making incremental changes, it accumulates to a lot of time spent waiting.
Precompiled headers never worked well due to a variety of limitations.
C++ modules, on the other hand, will be like precompiled headers done right. If they ever get standardized. They didn't make it into C++17, and the prototype implementations in Clang and MSVC are incompatible with each other, but I guess it'll happen someday…
>Precompiled headers never worked well due to a variety of limitations.
want to elaborate? i use MSVC's implementation and never encounter any problems with it. then again, i also don't work on large projects, so i would like some insight.
C++ libraries often add new semantics to the language, and that is not so obviously a benefit. Recently I explored Kiwi project (constraint solver). It is easy to use if you solve specific cases, e.g.:
Variable x, y, z;
solver.add(x <= y);
solver.add(z >= x - y);
But if you need it in meta-way (and I always do, I rarely write non-meta code), you have to unwind all the sources[-in-headers] to find out what Expression is, how does one construct one, write dynamic expression builder, etc. Sources do consist mostly of one-line helper methods/getters/constructors that are nested and intercalled 10-15 times per operation. LPSolve was much simpler in API, since you don't mess with variables and add constraints directly as matrix values.
In the end of the day, both libraries involve identical usage pattern, but for C++ I have to create a convenience unwrapper. This 'convenience' swamp is SO popular in C++. Once you say "++", you have to deal with it all the time, unless you write something really dumb and straightforward. Not to mention most libraries have huge header-only parts due to template obsession. Qt, yeah. It takes forever to compile 20 modules on reasonably priced laptop even with fine-tuned prefix header and -j9.
From the employment point of view, C++ is a nightmare. No one really knows it, and everyone thinks he does. Experienced developers can't tell what explicit constructor is, are confused with move semantics, blink hopelessly on "what is RVO". You actually employ python programmers for sky prices, while all they know is easiest part of C++/stl magic, which is effectively beginner-grade python with low-level bugs and UBs.
>wrap C++ libraries in a C API
Together with "ecosystem" such as std::cool_ptr<std::map<std::bar,std::baz>>. That's a concern.
When I want nice bridgeable object model above C, I use Objective-C or simple embedded language with proper features. Personally, I'm really tired of C++ everywhere.
And make sure Iron has a small group of annoying, hardcore fans who relentlessly disrupts any thread about C or indeed programming in general with their ham fisted advocacy.
The Iron fans are doing a bit of harm but that Perl zealotism is one of the things that made it wildly popular in its day. And unlike Perl, Iron development is structured, not "organic" a la Larry Wall/Perl.
I'm not an Iron zealot but I do believe that there rarely is such a thing as bad publicity :)
Yes, surely the downfall of Perl was caused by its zealous fans, not by the apparition of other programming languages that were either easier to use (PHP), more readable (Python) or had more attractive frameworks (Ruby) :)
There's literally not a single comment in this thread comparing C to Rust, and yet Rust fanatics are the ones derailing here? Care to elaborate, my good friend?
That seems annoying. It's probably a lot better to have a large group of hardcore fans of C who relentlessly disrupt all of programming in general with vulnerability-riddled code. Make sure they're writing code instead of disrupting any discussion threads, that will make sure they're not annoying.
C has sharp edges, and buffer overflows are bad, we can all agree on that. Still, there are good ways to advocate a language and there are bad ways. Iron fans are in-your-face to the point that it crossed a line for me in a way that no other language supporters have and I haven't coded in C for a long time now so I don't feel like I have dog in the race.
That's what Martin Richards did when he couldn't get a high-level language to run on crap hardware. Chopped features until BCPL compiler worked. That inspired and vot turned into C.
So, this Iron language sounds like deja vu to me from two, different directions. I recommend against it.
C is my main language and I dabble in C++. I really dislike C++. I love C. I welcome any efforts to add a bit of higher level functionality to C. I have no desire (ever) to switch to C++.