[HN Gopher] Goodbye C++, Hello C
___________________________________________________________________
Goodbye C++, Hello C
Author : khoobid_shoma
Score : 229 points
Date : 2021-06-25 22:56 UTC (1 days ago)
(HTM) web link (momentsingraphics.de)
(TXT) w3m dump (momentsingraphics.de)
| mauricedenassau wrote:
| While I can understand the feeling of "bloat" in C++, I can't
| help but be put off each time I have to implement something even
| remotely serious in C. I have to reinvent the wheel everytime
| while in C++ I can have access to high quality implementation in
| the STL.
| adamdusty wrote:
| The author lists GLFW, stb_image, and ImGui as dependencies for
| their renderer written in C. Those are the same exact
| dependencies you would use to write a renderer in C++. Maybe you
| would add glm because you don't want write matrix math
| operations, but the author will have to do that in C anyway. I
| don't see how dependencies are an advantage for C.
| acmj wrote:
| You can go down the route to say C++ is largely a superset of
| C. You can always write C++ in the C way, so you don't see
| simplicity is an advantage of C.
| bruce343434 wrote:
| > You can always write C++ in the C way
|
| Nope. They have grown too far apart. My C11 codebase simply
| does not compile when `mv *.c *.cpp && g++ main.cpp`.
| kaba0 wrote:
| That's why grandparent wrote "largely"
| bruce343434 wrote:
| Their second statement is still simply wrong. C has some
| ways of doing things which are simply unacceptable in
| C++. In C, you don't cast mallocs. You can initialize
| structs like `={0}` or `={prop=val}`. There's more, but
| the point is: if you are writing C++, you can't write it
| in a "C way". It won't compile at some point.
| kaba0 wrote:
| I interpreted the "C way" as in avoiding RAI, classes,
| references, etc. Just writing functions with structs and
| pointers. Not as "syntactic C"
| everyone wrote:
| I just freakin' _hate_ .h files... Writing pretty much the same
| thing but twice and in two different places for every function.
| Why?!? I would love C++ if not for them.
| pjmlp wrote:
| Then use modules, VC++ 20219 has the best support, GCC 11 has
| similar support, it is mostly clang that still had some catch-
| up to do.
| efficax wrote:
| For modules in GCC you currently have to build the branch
| yourself. Not the best experience
| jart wrote:
| Or use traditional C. The x86_64 System V ABI is well defined
| enough that I'm honestly not sure why we need prototypes. All
| we need is a compiler that authorizes us to use an ILP64 data
| model. Then we'll be able to write C the way it was intended
| to be written without the quadratic header complexity. C++
| showed us where that path leads and I truly hope modules
| happen but it's about as likely as my hopes of restoring the
| good parts of K&R C.
| gargalatas wrote:
| why someone would compare python wih c++?
| tayistay wrote:
| The article makes me wonder: how much headroom do compilers of
| newer slow-to-compile languages such as Rust or Swift have for
| improving compile times?
| yoz-y wrote:
| Swift can reduce the "max time to compile a complex expression"
| indefinitely to force programmers to write simpler-to-compile
| code. /s
| adrianN wrote:
| Nicholas Nethercote has a series of blog post on speeding up
| the Rust compiler. [1] There are (were?) also efforts going to
| to reduce the amount of LLVM work by shifting some of that work
| further up in the stack.
|
| [1]
| https://blog.mozilla.org/nnethercote/author/nnethercotemozil...
| the_duke wrote:
| In addition to the mentioned micro optimizations, I'll add some
| possibilities from the Rust angle.
|
| Rust has really impressive incremental compilation built in
| already.
|
| The first compile is annoyingly slow, but additional ones will
| be surprisingly fast, often finishing in a second or two , even
| in larger code bases. Especially if you split up your own code
| into multiple crates.
|
| This could further be significantly improved by a demonized
| compiler that keeps everything in memory.
|
| Additionally there could be a way to share compilation
| artifacts via the package registry, also making first time
| compiles fast.
|
| There is also an effort to use Cranelift as a codegen backend,
| which can be faster than llvm for debug builds, and has the
| potential for JIT compiling functions on first use, which would
| make debug builds a lot faster again as well.
|
| Zig also has some really cool plans in this domain.
|
| There is lots of headroom to make compiling these static
| languages faster, but compilers would often need to be
| (re)written from scratch with these compilation strategies in
| mind.
| jart wrote:
| If you want a good case study in repositories that build quickly
| then check out cosmopolitan libc. Typing `make -j16` it compiles
| 15,383 .o objects, 69 .a static libraries, and 548 executables,
| 297 of which are test executables, which are also run by the make
| command, and all that takes just 40 sec.
| qayxc wrote:
| Minor nit-pick: wouldn't that rather be _make -j`nproc`_? Not
| everyone has an 8-core CPU with SMT or a 16+-core CPU
| respectively.
| jart wrote:
| I measured that on an Intel(R) Core(TM) i9-9900. I understand
| that not everyone has the privilege of being able to afford a
| $1,000 computer. However those people usually aren't coders.
| It'd be great if I had one of those 64 core "specialist"
| workstations that people like googlers use to be able to
| compile projects like TensorFlow in a reasonable amount of
| time. My project would probably build in seconds. But alas I
| don't, so I need to forgo eigen and template metaprogramming.
| maleldil wrote:
| > Where C++ gives you many slightly different takes on the same
| concept (e.g. unique_ptr, shared_ptr and raw pointers), C gives
| you one way to go and that one has a conveniently compact
| notation (such as float _).
|
| You use unique_ptr and shared_ptr because float_ is unsafe. If
| you don't care about the safety that smart pointers provide, you
| can use float* in C++ too.
|
| > ticking with the example of matrix math, it is baffling how C
| code is often more compact and readable than C++ code. A matrix
| declaration would be float matrix[4][4]; [...] Of course, you
| could take the same approach in C++ but then you are not really
| using C++. The example from above in C could be simply: float*
| top = matrix;
|
| But that's not that same thing, is it? I fail to see how you're
| actually getting the top 3 rows there; you're just assigning a
| pointer. Again, you're making your code less safe and expressive
| by using no abstraction at all.
|
| And please, how is `matrix_add(matrix_mul(A, B), c)` more compact
| and readable than `A*B + c`? And this is being kind to the C
| version.
|
| If the problem is with C++'s compilation times, then it's
| perfectly reasonable; this is one of the major problems of the
| language. But using unsafe constructs and then saying that
| they're better than C++'s safer alternatives is just comparing
| apples to oranges.
| bruce343434 wrote:
| Markdown ate some of your asterisks
| giomasce wrote:
| The problem is not even just safety. You can write safe C, if
| you are careful (and you need to be careful also for C++, smart
| pointers do not just magically make everything right). The
| point is that OP makes the point that C requires to write less
| code, and this doesn't even seem true: you have to remember,
| each time some non-trivial object goes out of scope, to call
| its destructor/deallocator, which results in a lot of code
| (which can at times hard to read, especially if you have not
| completely linear control flow). Looking at just the
| declaration is a small part of the issue.
| Ygg2 wrote:
| > You can write safe C, if you are careful
|
| You can also live forever, if you have right combination of
| genetics and environment.
| z0ltan wrote:
| You analogy makes no sense. By your measure, nothing is
| safe.
| ksaj wrote:
| Like when someone says "Lisp is homoiconic," and that
| automagically creates a thread of people tripping over
| themselves to make the same points they do every time
| "Lisp" and "homoiconic" appear in the same sentence.
|
| There should be a list of these language snowball
| avalanches somewhere. They work every time.
| Ygg2 wrote:
| I assume Lisp homoiconicity is closer to fact than "You
| can write safe code in C".
|
| I mean it is possible. In the same way all molecules of
| air could bunch up in one corner of the room suffocating
| you. It's a possibility.
| UncleMeat wrote:
| I don't believe that it is possible to write safe C (or C++),
| even if you are both very careful and also among the most
| skilled C developers out there. Every sizable project in C
| has had critical vulnerabilities. It is not possible to train
| engineers on a team of any meaningful size to consistently
| write bug free code and catch bugs in code review.
| Sanitizers, fuzzing, and static analysis all help but are
| insufficient in the face of the utterly impossible task of
| writing safe C programs, let alone _evolving_ safe C
| programs.
|
| Look at all the very smart people that tried and completely
| failed to write libraries that do such basic things as
| copying strings.
| MaxBarraclough wrote:
| > You can write safe C, if you are careful
|
| In practice this pretty much cannot be done unless you use a
| formal verification framework, which almost no one does. Even
| the most well-resourced projects written in C tend to have
| trouble with low-level bugs. Same goes for C++. Both the
| Linux kernel and Chromium have plenty of these issues.
| giomasce wrote:
| My comment seems to have sparked a lot of reaction about
| safety, but that wasn't the main point. The main point (and
| the one OP talks about) was about conciseness, and I can't
| really see how C can be considered a concise language.
| MaxBarraclough wrote:
| Agreed. C is more concise than assembly, but it's highly
| verbose compared to most modern languages. C++ can
| sometimes be a good deal more concise.
| skohan wrote:
| It depends. One approach to avoiding tricky memory management
| issues is to avoid memory management altogether. In the kind
| of applications where C is a good choice, you might consider
| strategies like allocating all the memory you need to work
| with besides small structs which fit on the stack when the
| program starts and then never deallocate. If you're worried
| about performance this is often a very good strategy.
| Jweb_Guru wrote:
| We are talking about a renderer. Renderers definitely need
| to deallocate or otherwise manage memory :)
| skohan wrote:
| Why would a renderer need to deallocate memory? And
| _when_ would a renderer need to deallocate memory?
| Jweb_Guru wrote:
| Renderers need to deallocate memory all the time. For
| example, they often need to store per-frame data that
| gets dropped when the frame is over, debug strings, track
| temporary access to texture/buffer data, manage command
| buffers, etc. They also have to track resources for lots
| of data types external to the program (such as GPU data)
| which often forces them to work with external allocators,
| as well as having to synchronize stuff like resource
| destruction using GPU-CPU fences. Moreover, most
| resources are either immutable or can be accessed
| optimally when they are immutable, so you often end up
| benefiting significantly from being able to destroy and
| reclaim the memory of stuff you're no longer using rather
| than trying to update in place.
|
| Even if you try to only allocate one buffer (for example)
| to split up and use for all your logical vertex buffers,
| you still have to manage memory within that buffer, which
| ends up leading to pretty much the same kinds of complex
| reasoning you need for ordinary dynamic allocation.
| Failure to do this properly results in resource leaks on
| both the GPU and CPU side which can be really nasty.
|
| Of course, this all depends on the complexity of your
| renderer. For simple usecases you might be able to get
| away with no deallocation until exit. But they'd have to
| be _really_ simple.
| habibur wrote:
| > Renderers need to deallocate memory all the time. For
| example, they often need to store per-frame data that
| gets dropped when the frame is over
|
| Zero the buff and reuse this same frame as the new frame.
| skohan wrote:
| > For example, they often need to store per-frame data
| that gets dropped when the frame is over, debug strings,
| track temporary access to texture/buffer data, command
| buffers, etc.
|
| Exactly. You can just bulk allocate all the memory for
| the frame at the beginning and drop the entire thing
| after the frame is finished. This is a very easy case for
| manual memory management where you have one allocation
| and one deallocation per frame.
|
| Or you can do one better, and keep an arena for each
| framebuffer, and then just recycle them and never
| deallocate at all. If you really need some level of
| dynamism in terms of memory usage, you can just double
| the size of the arena every time you fill it, and you
| still don't need to deallocate, since chances are you
| will need that space again for some frame in the future.
| Jweb_Guru wrote:
| I love arena allocation (which, incidentally, _does_
| entail dynamic resource destruction if you use it per-
| frame, and which is pretty easy to get wrong if you 're
| just storing raw pointers everywhere--so good bye nice
| use after free properties), but you absolutely cannot
| just use arena allocation for everything. GPU resources
| in particular need to wait on fences and therefore _can
| 't_ have a neatly defined lifetime like what you're
| proposing unless you block your whole program waiting for
| these fences, while other GPU resources are (like I said)
| immutable and need to live for multiple frames. Where
| exactly do you store this data? How do you access it? How
| do you free it when it's done? How do you perform slow
| tasks like recomputing pipelines and shaders in the
| background without being able to dynamically track the
| lifetimes of the old/new backing GPU allocations? More
| generally, how do you track resources other than the
| framebuffer/surface, which are usually managed very
| dynamically and often appear and disappear between
| frames?
|
| I think maybe it would be helpful if you would go look at
| the actual implementation of a nontrivial renderer and
| try to find one that doesn't allocate. Because the
| strategies you're describing don't match my experience
| working on renderers, at all.
| skohan wrote:
| I am speaking from experience, I have written a lot of
| renderers.
|
| > GPU resources in particular need to wait on fences and
| therefore can't have a neatly defined lifetime like what
| you're proposing unless you block your whole program
| waiting for these fences, while other GPU resources are
| (like I said) immutable and need to live for multiple
| frames.
|
| I don't quite understand why fences are a problem here.
| As long as you know what frame a resource corresponds to,
| you just to have to guarantee that the entire frame is
| finished before reusing. You don't need to know what's
| happening at a granular detail within the frame.
|
| As far as static resources, that's easy: another arena
| for static resources which grows as needed and is shared
| between frames. Most of that data is living on the GPU
| anyway, so you just need to keep a list of the resource
| handles somewhere, and you're going to want a budget for
| it anyway, so there's no reason not to make it of finite
| size.
|
| > More generally, how do you track resources other than
| the framebuffer, which are usually managed very
| dynamically and often appear and disappear between
| frames?
|
| See the whole issue is trying to solve the issue "more
| generally". In many cases, you can know exactly the
| resources you will need (or at least the upper limit)
| when you start rendering a given scene. If you need to do
| any memory management at all, you can do it at neatly
| defined boundaries when you load/unload a scene.
|
| The only time when you need some kind of super dynamic
| renderer is when you are talking about an open world or
| something where you are streaming assets.
|
| Most of the serious renderers are written in C++ using
| custom allocators, which are just some kind of arena
| allocator under the hood anyway.
| Jweb_Guru wrote:
| Sorry, but if you think you can just get away with
| _never_ deallocating any static GPU resources (as you 're
| implying by "just use an arena"!), one of two things is
| going on:
|
| (1) You are manually managing an allocator on top of the
| GPU resources in the arena (reintroducing the usual use
| after free problems). (2) You have written a highly
| specialized renderer that cannot be reused across
| programs, as it requires all GPU resources used in any
| scene to fit into memory at once (and for their exact
| quantity to be known).
|
| Once you acknowledge that asking for dynamic GPU resource
| allocation, the rest of what I asked about follows; for
| instance, you can't reuse whatever portion of the
| already-allocated resources were freed at the end of the
| frame unless you can track used resources across frames;
| you can then either trace the open command buffers each
| frame or whatever (tracing all the live resources) or use
| something like reference counting and free once the fence
| is done (tracing all the dead resources). Hopefully you
| will acknowledge neither of these is the same as arena
| allocation.
|
| "dynamic GPU resource application isn't needed for a
| useful renderer" certainly sounds good, but again does
| not jive with my experience with renderers (admittedly,
| most of this involves rendering an open world with
| streaming assets, but most examples I've seen that aren't
| super tightly constrained examples require resource reuse
| of some sort).
| skohan wrote:
| I think you are not thinking through all the
| possibilities here, but you would be surprised how far
| you can get with this approach
| gwmnxnp_516a wrote:
| It is possible to write safe-C, but C is far more error prone
| than C++. C has more implicit type conversions than C++,
| which may results in bugs and undefined behaviours. C lacks
| RAII (Resource-Acquisition Is Initialization) that is useful
| for memory and resource management. C will actually require
| more code than C++ since, the C standard library lacks
| generic data structures such as vectors, hash maps, linked
| lists and so on. The implementation of those data structures
| requires lots of preprocessor macro hacks.
|
| It is possible to write safe C, only if one uses static
| analysis tools and undefined behaviour sanitizers. In the
| case presented by the article, as it is related to game,
| safety does not matter much, unlike device drivers, operating
| systems or embedded systems where C bugs can introduce
| security vulnerabilities.
|
| Regarding the compile-time complaint, it is possible to
| reduce the compile-time by using forward declarations;
| forward template declarations; template forced instantiation
| and isolating large parts of a project in a static library
| using CMake.
| Koshkin wrote:
| > _C is far more error prone than C++_
|
| I don't know about that... C++ (both the language and the
| library) is orders of magnitude more complex, and the
| opportunities to make mistakes have grown almost
| proportionally. (Two characteristic examples recently
| discussed here on HN: auto references and iterator
| invalidation.)
| kaba0 wrote:
| There are two problems here, one being complexity, the
| other being abstraction power. The two correlates. C is
| an easy language without much complexity, but with
| laughably little abstraction power. Text-based macros are
| the worst thing ever, and other than that, the language
| can't even express reusable data structures, only with
| convention.
|
| C++ on the other hand has good expressivity that can
| better deal with complexity of programs (eg. just simply
| having string, vector, etc) at the expense of some added
| language complexity. But unfortunately most of that is
| due to backward compatibility.
|
| My opinion is that program complexity is inherent for
| anything interesting, so the C++ tradeoff is worthwhile.
| Also, by sticking to a good subset of C++, one can
| minimize the "bad" complexity of the language, imho.
| gwmnxnp_516a wrote:
| But C lacks even strings, lots of C bugs and
| vulnerabilities are related to memory management, memory
| ownership and string handling. Even the C subset of C++
| is better than C since it at least has more explicit type
| conversions that forces the developer to state his or her
| intent. One example of the C string problem is the
| strcpy(buffer, char* string) that copies a string to a
| buffer. If an external actor discovers how to manipulate
| the string size, he or she can take advantage of this
| buffer overflow vulnerability and even execute arbitrary
| code remotely if it is used in a server. If the
| application with this problem is a file, one create a
| specially crafted file to take advantage of this design
| flaw.
|
| However using C in the case of the original poster does
| not matter much as the application is game-related not
| subject to untrusted input.
| jimbob45 wrote:
| I agree he used a trash example. I would offer include
| guards/pragma once as a slightly better example.
| badsectoracula wrote:
| FWIW i've seen a lot of 3D codebases in C++ that avoid operator
| overloading and instead use something like
| `A.multiply(B).add(c)` to make more explicit what is going on.
| Some even break that into multiple calls instead of using a
| single expression.
| qalmakka wrote:
| The confusion created by operator overloading is easily
| solved by tooling. The compiler always knows what a given
| operator resolves to, and environments like Visual Studio
| Code with the right plugins can handily highlight what you
| are actually calling.
|
| Also I think that like everything in C++ it's a matter of not
| abusing a given feature. It's still useful to have the
| ability to overload operators because it makes the intent
| behind certain operations much clearer. There's nothing worse
| than not being able to address an ArrayList in Java with the
| subscript operator.
|
| Nowadays I don't use operator overloading in C++ a lot, it's
| mostly `=`, `==`, `<=>`, `bool`, `*`, `->`.
| maccard wrote:
| That's still better than matrix_add(c, matrix_mul(b, a))
| though - you can't do A.mul(B).add(C) in C
| framerate wrote:
| You'll find a lot of SIMD code using the multiple calls
| approach as for far too long C++ compilers did daft things
| like transfer the SIMD registers back to the stack or refuse
| to inline when the expression got slightly interesting.
| jolux wrote:
| I fail to understand how that's more explicit to be perfectly
| honest. If it's hard to resolve where overloads are coming
| from then that's a problem but otherwise it just seems more
| verbose for no benefit. Addition and multiplication are the
| operations in use here, why shouldn't we use their operators
| to represent them?
| humanrebar wrote:
| I like operator overloading, but it is susceptible to
| argument dependent lookup while member methods are not. The
| syntax is literally more explicit about what logic is being
| called.
| gravypod wrote:
| In situations where the output's shape changes based on the
| operation this can be helpful. auto
| accumulate = ...; auto a = ... auto b = ...
| auto result = a*b; for (int row = 0; row < a.rows)
| { accumulate += result[row] }
|
| This expression might be entirely valid when a and b are
| both 2x2 but then becomes incorrect when there's a
| different shape in b.
| CyberDildonics wrote:
| Do you mean type instead of shape? I also don't know what
| you mean by incorrect. C will also create casts when
| using two different number types in a math operation.
| gravypod wrote:
| For matrix and quaternions multiplication does not output
| the same type as the input.
|
| For example: int a = 1;
| a + a = 2; (int) a * 2 = 2; (int)
| auto ma = matrix<2, 4>(); auto mb = matrix<4,
| 1>(); ma * a is matrix<2, 4> ma *
| ma is not possible ma * mb is matrix<2, 1>
|
| Compilers used to be very bad at telling you what was
| going on here if you, for example, changed `<2, 4>` to
| `<3, 4>` or something. g++ now handles it pretty well:
| https://hastebin.com/idemopikag
| CyberDildonics wrote:
| Your original comment was about something being
| incorrect, now you are talking about error messages, but
| I don't think either has to do with operator overloading.
| duped wrote:
| There is more than one definition of a matrix or vector
| product and whichever one is most obvious can be
| contextual.
| userbinator wrote:
| "safety" is really overrated. The paranoia-fueled security
| industry has turned programming into some sort of weird
| authoritarian dystopia.
| z0ltan wrote:
| You've done it now. Get ready for the vengeance of the RDF
| (Rust Defence Force).
| wyager wrote:
| Is this a joke? Do you really think _managed pointers_ are an
| example of authoritarian dystopianism?
| greesil wrote:
| unique_ptr is a conspiracy, man. You see, the people who
| make money off of mallocs (those corrupt DRAM
| manufacturers) want it to stay that way. Just follow the
| money.
| aksss wrote:
| Thread safety is like bicycle helmet laws, discuss..
| cjfd wrote:
| Well, kind of. I would argue that most code does not need
| to be thread safe because it is not intended to run in a
| multi-threaded environment. I once worked on a
| application where it was 'standard' to run everything in
| a thread pool so basically everything could run
| simultaneous to everything else. Problem was that there
| also was lots of state to manage. So then one ends up
| giving every class one or more locks. Also, this
| particular application was not the high-performance part
| of the application. Obvious solution is to run most of
| the application in a single thread message loop and get
| rid of all of the locks. This appears to be heresy
| nowadays though. The high profile C++-ers tell us that
| everything has to be thread safe.
| Jweb_Guru wrote:
| > Well, kind of. I would argue that most code does not
| need to be thread safe because it is not intended to run
| in a multi-threaded environment.
|
| In that case you are actually thread safe, though! Just
| use a language that lets you specify which data can't be
| sent across threads (Rust isn't the only example, Erlang
| enforces this entirely dynamically) and use thread locals
| instead of statics (in a single threaded environment
| they're effectively the same thing), and tada, you have
| thread safety that continues to work even if people
| decide to run your stuff on many different cores.
| beached_whale wrote:
| One would, but without a barrier the head hit before
| slowing waiting for the group the ground to get out of
| the critical section :)
| Santosh83 wrote:
| He is talking (I believe) about the general trend of
| nudging, cajoling more and more coders into using managed,
| very high level, safe languages and runtimes, and in
| general discouraging peeking under the hood, at the
| hardware level, as something raw, wild or unsafe. Yes you
| can still do it on a RPi, but perhaps in another decade or
| so, you might not be allowed to program in 'unsafe'
| languages on all other mainstream platforms, unless you
| register for a driver/system developer license or
| something, or not even that.
|
| The tinkerer/hacker ethos is disappearing slowly from PCs.
| It never caught on in the mobile world. It may perhaps only
| survive as a remnant in specialised chips and boards
| designed for learning.
| maccard wrote:
| to be clear, you're claiming that language constructs for
| avoiding massively prevalant use-after-free bugs
| (unique_ptr) will lead us all to lose control of our
| devices?
|
| Nobody's suggesting we replace all the C in the world
| with signed javascript from Google, we're literally
| talking about compile time checks for pointers here.
| jolux wrote:
| > the general trend of nudging, cajoling more and more
| coders into using managed, very high level, safe
| languages and runtimes
|
| This is a good thing: these languages and runtimes are
| indeed much safer, and also much more productive than C.
| You can even still get the same amount of low-level
| control with Rust.
|
| > in general discouraging peeking under the hood, at the
| hardware level, as something raw, wild or unsafe.
|
| C _is_ unsafe though. Decades of experience writing
| software has shown us that even expert C programmers
| write memory bugs with regularity.
|
| > Yes you can still do it on a RPi
|
| Or any other PC really.
|
| > but perhaps in another decade or so, you might not be
| allowed to program in 'unsafe' languages on all other
| mainstream platforms, unless you register for a
| driver/system developer license or something, or not even
| that.
|
| Lunacy. What is the evidence for this?
|
| > The tinkerer/hacker ethos is disappearing slowly from
| PCs.
|
| It was only ever there in the first place with a tiny
| minority of users, and that minority seems as committed
| to their craft as they've ever been.
| userbinator wrote:
| _Lunacy. What is the evidence for this?_
|
| Look at all the locked-down walled-garden platforms
| proliferating, and this famously prescient story:
| https://www.gnu.org/philosophy/right-to-read.en.html
|
| 20 years ago, many people thought RMS was a completely
| insane lunatic. Yet now he seems more like a prophet.
|
| It's not hard to see where things are going if you read
| between the lines. Increasingly, "safety and security" is
| being used to exert control over the population and
| destroy freedom. Letting your children play outside
| unsupervised is "unsafe". Non-self-driving cars are
| "unsafe". Eating certain food is "unsafe". Having a
| rooted mobile device is "unsafe". Not using an approved
| browser by a company that starts with G is "unsafe". ...
| Programming in C is "unsafe".
|
| "Freedom is not worth having if it does not include the
| freedom to make mistakes."
| mwcampbell wrote:
| > "Freedom is not worth having if it does not include the
| freedom to make mistakes."
|
| That's only true to a point. Many mistakes are costly,
| and those costs are often born by other people. So it's
| reasonable to have protection against mistakes, for the
| benefit of both the person who would make them and the
| other people that they would affect.
|
| When it comes to computer security in particular, an
| easily compromised personal computer can be devastating
| to the livelihood of the person whose computer was
| compromised through no fault of their own (remember, most
| people don't know anything about computer security, _and
| they shouldn 't have to_), and can also harm others, e.g.
| if the computer becomes part of a botnet. If that
| computer is part of an organization, then the mistake
| made by a programmer can affect the ability of that
| organization to provide important, even essential,
| services. _This_ is what 's driving the increased focus
| on safety in this context.
|
| I realize we're drowning in cynicism these days, and it's
| tempting to think that it's all an evil conspiracy to
| take away our freedom so a few people can make more money
| or have more power. Such a narrative resonates with
| something primal in us that's reinforced by the sort of
| simplistic good versus evil stories that make up so much
| of our entertainment. Reality is messier, more nuanced,
| and not as neatly connected as our puny pattern-seeking
| brains would prefer.
| pjmlp wrote:
| 20 years ago many people earned a living selling
| commercial compilers, and the PC was the exception to
| vertical integration.
|
| Plenty of people knew what RMS was talking about.
|
| But GPL was very bad for business, said those there were
| against it, so the new world of shareware and public
| domain is here again.
| woodruffw wrote:
| > Look at all the locked-down walled-garden platforms
| proliferating
|
| I don't think I'm getting the connection here --- Rust
| was incubated at Mozilla and is now managed by its own
| open-source foundation. There's nothing particularly
| closed or "walled garden" about it.
|
| By contrast, Apple's ecosystem is the canonical example
| of a walled garden. But it's overwhelmingly programmed in
| unsafe languages (C, C++, and Objective-C). So what
| gives?
|
| > It's not hard to see where things are going if you read
| between the lines. Increasingly, "safety and security" is
| being used to exert control over the population and
| destroy freedom
|
| This is an eye-poppingly confusing confabulation: in what
| world am I any less free because the programs I write and
| use have fewer trivial vulnerabilities in them? What
| freedom, exactly, have I lost by _choosing_ to crash
| less?
|
| You bring up the GNU project; their background is
| explicitly rooted in Lisp: one of the _very first_ safe,
| managed languages. The unsafety and comparative messiness
| of C is one of their standard bugbears. That hasn't
| stopped their message of political and software freedom,
| as you've pointed out.
| pjmlp wrote:
| Actually GNU project is one of the culprits for C
| spreading into a world of that was already moving into
| C++ and other safer languages.
|
| > When you want to use a language that gets compiled and
| runs at high speed, the best language to use is C. C++ is
| ok too, but please don't make heavy use of templates. So
| is Java, if you compile it.
|
| https://www.gnu.org/prep/standards/html_node/Source-
| Language...
|
| 20 years ago, it was more like
|
| > When you want to use a language that gets compiled and
| runs at high speed, the best language to use is C. Using
| another language is like using a non-standard feature: it
| will cause trouble for users. Even if GCC supports the
| other language, users may find it inconvenient to have to
| install the compiler for that other language in order to
| build your program. For example, if you write your
| program in C++, people will have to install the GNU C++
| compiler in order to compile your program. > > C has one
| other advantage over C++ and other compiled languages:
| more people know C, so more people will find it easy to
| read and modify the program if it is written in C.
|
| http://gnu.ist.utl.pt/prep/standards/html_node/Source-
| Langua...
|
| Thank GNU for C.
| woodruffw wrote:
| > Actually GNU project is one of the culprits for C
| spreading into a world of that was already moving into
| C++ and other safer languages.
|
| Both of these things can be true! GNU has advocated for C
| for some pretty asinine reasons. At the same time,
| they've ported all kinds of Lisp idiosyncrasies into
| their style guide.
| loup-vaillant wrote:
| With the possible exception of Rust, safety always had
| performance implications. Forcing people to write their
| program in C# or Swift or Java causes many programs to be
| slower than they really need to be, forcing us to either
| wait on them, or buy a faster palmtop.
|
| (Now most devs don't care about performance, so they
| don't see that as a problem. As a user however I can tell
| you, I _hate_ when my phone lags for seemingly no good
| reason.)
| woodruffw wrote:
| > With the possible exception of Rust, safety always had
| performance implications.
|
| This is common piece of received wisdom, but I don't
| think it's held up well over the last decade: both Java
| and C# have extremely well-optimized runtimes that
| perform admirably after an initial startup period, and (I
| believe) Swift compiles to native code with many of the
| same optimization advantages that Rust has (e.g., simpler
| alias analysis).
|
| At the same time, C++ has seen a proliferation of
| patterns that are _slightly_ safer, but perform miserably
| at scale: smart pointers (unnecessary lock contention),
| lambdas (code bloat, more I$ pressure), templates (I$),
| &c. C avoids most of these, but C written by "clever"
| programmers has similar pitfalls.
| skohan wrote:
| > these languages and runtimes are indeed much safer, and
| also much more productive than C. You can even still get
| the same amount of low-level control with Rust.
|
| Rust is not a C analog. The whole value proposition of C
| is simplicity, and Rust is anything but simple.
|
| >> but perhaps in another decade or so, you might not be
| allowed to program in 'unsafe' languages on all other
| mainstream platforms, unless you register for a
| driver/system developer license or something, or not even
| that.
|
| > Lunacy. What is the evidence for this?
|
| Look at a platform like Apple. Every release makes it
| harder to run arbitrary code.
|
| >> The tinkerer/hacker ethos is disappearing slowly from
| PCs.
|
| >It was only ever there in the first place with a tiny
| minority of users, and that minority seems as committed
| to their craft as they've ever been.
|
| What do you mean? In early PC's, the way you ran software
| was to copy code from a magazine and compile and run it
| on your workstation. Being a PC user _at all_ meant being
| a tinkerer /hacker a few decades ago.
| bfrog wrote:
| Rust is arguably simpler than C++ to comprehend, and
| sure, more complex than simple C.
|
| But the complexity argument is overblown.
| skohan wrote:
| I didn't say anything about C++.
|
| Rust is a very complex language. You can argue about
| whether it's more or less complex than C++, but it's
| certainly on that end of the spectrum. C is way on the
| other end.
|
| That's not a value judgement of Rust, just an
| observation.
| woodruffw wrote:
| > C is way on the other end.
|
| Rust is complex, but I think it's honest in its
| complexity: it straddles programmers with lifetime
| management in exchange for better optimizations (alias
| analysis is a pain in C!) and memory safety.
|
| This is in contrast to C: it's very easy to write C that
| compiles, but very difficult to fully evaluate its
| correctness. Part of that is the _extraordinary_
| complexity of the C abstract machine, combined with its
| leakiness: writing correct C requires you to understand
| both your ISA's memory model and the set of constraints
| inconsistently layered on it by C. That's a kind of
| pernicious complexity that Rust doesn't have.
| jolux wrote:
| > Rust is not a C analog. The whole value proposition of
| C is simplicity, and Rust is anything but simple.
|
| I would say the value proposition is control and
| performance, and more pragmatically ubiquity. If the
| value proposition were simplicity, why aren't C
| programmers writing Lisp instead? If it's simplicity and
| control, why aren't they writing assembly? At this point,
| C is little more than a bad abstraction that people are
| nostalgic for.
|
| > Look at a platform like Apple. Every release makes it
| harder to run arbitrary code.
|
| No, it doesn't. It has not gotten harder to run arbitrary
| code. It has gotten harder for developers to distribute
| unsigned applications. I've been using Macs for 10 years
| and my setup process throughout that whole time has been:
| xcode-select --install, install Homebrew, get on with my
| life. The OS never interferes with my programming beyond
| that.
| catblast01 wrote:
| > In early PC's, the way you ran software was to copy
| code from a magazine and compile and run it on your
| workstation. Being a PC user at all meant being a
| tinkerer/hacker a few decades ago.
|
| Bullshit. Except for the brief period of time when the
| Altair was the only thing going on in the Micro space...
| the Apple II, Atari 800, IBM PC and TRS-80 amongst others
| were marketed in the late 70s/early 80s with off the
| shelf, ready to run software. While copying code out of a
| magazine was something you could do, it wasn't even the
| common case then.
|
| > Every release makes it harder to run arbitrary code.
|
| I have not experienced this. Yes Mac OS makes it harder
| to run random stuff downloaded from the internet, but
| Llvm, clang, cmake, python from the command line works
| the same as they always have (you are fetishizing code
| that is entered yourself after all).
| skohan wrote:
| The new windows does not even run on hardware which
| doesn't have TMP. You really don't see signs that
| computers are getting more closed?
| pjmlp wrote:
| PC was an accident caused by IBM's failure to put Compaq
| into line.
|
| All other platforms were hardly any different from Apple,
| in fact Apple is just like they always have been
| forgotmypw17 wrote:
| >What is the evidence for this?
|
| There are entire CLASSES of computing devices which you
| cannot put arbitrary code on without severe obstacles...
| kaba0 wrote:
| How exactly is it relevant to the topic? What does it
| have to do with ditching C?
| patrick451 wrote:
| Safety above all is is the path towards slavery.
|
| This is true in both politics and software.
| kitd wrote:
| _This is a good thing: these languages and runtimes are
| indeed much safer, and also much more productive than C.
| You can even still get the same amount of low-level
| control with Rust._
|
| How do you bootstrap languages like Rust? Another 'safe'
| language? What about that one?
|
| Someone somewhere has to be working at the asm level.
| dthul wrote:
| Bootstrapping and language safety are orthogonal. C is
| unsafe and still you can't bootstrap it if you don't
| already have a compiler which can compile your C
| compiler. According to that logic even assembly is not
| low level enough because you need an assembler to make a
| runnable program out of it.
| pjmlp wrote:
| Source code available,
|
| http://www.projectoberon.com/
| nemothekid wrote:
| Safety is complete orthogonal to being a bare-metal
| language (See Rust). You can have a completely locked
| down platform with an unsafe language (See iOS).
|
| I'd argue that anyone who thinks language safety is some
| authoritarian handcuff doesn't really understand low-
| level programming to begin with.
| skohan wrote:
| I actually think safety should be guaranteed at an
| OS/hardware level and not at a software level. If it's
| guaranteed that my process can only make a mess inside
| it's own memory allocations, let the software be as
| unsafe as it wants.
| Thiez wrote:
| Then you'll be happy to learn that what you propose has
| been the case for consumer computers since protected mode
| was added to Intel 80286 processors in 1982.
|
| I think few people in this discussion are worrying about
| programs directly affecting other programs through memory
| unsafety, exactly because this doesn't really happen for
| software that isn't a driver or inside the OS kernel. The
| problem with memory unsafety is that it often allows
| exploits that corrupt or steal user data, or gain
| unauthorized access to a system. That's not a problem
| when you are the only user of your software and you only
| have access to your own data, but once you have other
| peoples data or run on other peoples system I think you
| should at least consider the advantages of using a
| safe(r) language.
| skohan wrote:
| But I don't understand how data stealing can happen if
| each process is effectively sandboxed. If my process
| can't read or write to memory outside of what it
| allocated, how can I corrupt or steal user data?
| Thiez wrote:
| Well it depends on your definition of sandboxed. Does
| your program have permission to perform I/O, either by
| reading/writing to a filesystem or sending/receiving data
| on a network?
|
| Most "interesting" programs can perform I/O. Then you run
| into ambient authority and confused deputies.
| skohan wrote:
| Yeah I guess it seems like a decent model for "safe"
| software would be sandboxed memory, and fine-grained file
| permissions. Arbitrary network traffic is a bit less
| dangerous - I mean someone could steal CPU cycles to
| process data and send it over network, but a safe
| language is not going to save you from that either.
|
| Most programs do not need arbitrary access to the file
| system, and it should be the OS's job to whitelist which
| files a program has access to. Again, a safe language is
| not going to save you from bad behavior on the filesystem
| either. It really only solves the memory problem.
| Thiez wrote:
| > Most programs do not need arbitrary access to the file
| system, and it should be the OS's job to whitelist which
| files a program has access to. Again, a safe language is
| not going to save you from bad behavior on the filesystem
| either. It really only solves the memory problem.
|
| Except that it is often a memory-safety problem that
| enables an attacker to make a program misbehave, through
| things like buffer overflows. A memory-safe program is
| much harder to exploit.
| skohan wrote:
| Are we talking in circles here? My original point was
| that memory safety should be ensured by the OS/hardware.
| That way no matter how badly my program misbehaves, it
| will not be able to access memory outside of the sandbox.
| In other words, the CPU should not be able to address
| memory which has not been allocated to the current
| process. A buffer overflow should be a panic.
|
| Even with a safe language, there's vulnerabilities like
| supply chain attacks which allow malicious code to use an
| escape hatch to access memory outside of the process.
| I.e. I could be programming in Rust, but one of the
| crates I depend on could silently add an unsafe block
| which does nefarious things. OS/hardware level sandboxing
| could prevent many such classes of exploits.
| kaba0 wrote:
| > That way no matter how badly my program misbehaves, it
| will not be able to access memory outside of the sandbox
|
| The problem is not about memory outside of the sandbox,
| but inside. Please read about return-oriented
| programming, for example, where a buffer overflow bug of
| a process can be exploited to hijack said process to do
| work it was not meant to do normally. If this error
| happened for example in sudo, it can very well be used to
| gain privileges and do real harm -- and this whole
| problem domain is almost entirely solved by using a
| "safe" language.
| kaba0 wrote:
| In case of a browser, a buffer overflow can be exploited
| to upload user files for example -- which are very much
| readable without trouble on most linux distros.
| gravypod wrote:
| "I know my software just works" is really hubris. The fly-by-
| the-seat-of-our-pants game industry has cranked up
| programmers egos and made them ignore a wide range of tools
| and practices that have been proven time and time again to
| improve developer velocity and reduce defects.
| userbinator wrote:
| "proven" sounds like cargo-culting dogma. The same was said
| of OOP in the 90s, and look what that caused. Hence my
| distrust of the snake-oil.
|
| Also, my real-world experience with wading through the
| abstraction insanity often seen in C++ (and justified
| because it's "safer") to find and fix bugs, and even more
| so with the sheer baroqueness of Enterprise Java (arguably
| an "even safer language"), shows that "reduce defects" is
| more like a dream. Maybe the number is reduced but when one
| is found, it tends to be harder to fix.
|
| Put another way, I'd rather fix relatively simple C (which
| also tends to be simpler code in general) than the monsters
| created by "modern C++" because they thought the "added
| safety" would mean they could go crazy with the complexity
| without adding bugs. Perhaps there is some sort of risk
| compensation going on.
|
| The saying "C makes it easy to shoot yourself in the foot;
| C++ makes it easy to blow the whole leg off" comes to mind.
| christophilus wrote:
| I think that if we focused on building small, simple
| programs that do one thing well and compose, C would be
| OK. It's when we build out behemoths that you really have
| a hard time reasoning about your code. At that point,
| vulnerabilities are almost guaranteed. This is true in
| any language, but more so in unsafe ones.
|
| Maybe the suckless guys are on to something.
| kaba0 wrote:
| The complexity that _must_ arise (otherwise the problem
| we are looking at is not interesting enough) will happen
| either way. Composing small tools will give you an ugly
| as hell glue code over them -- just imagine a modern
| browser. Would it really be better to do curl and
| interpretJS and buildDOM and all these things? Just
| imagine writing the glue code for that in what, bash?
|
| We pretty much have exactly that, but better with
| programming languages composing libs, functions and other
| abstractions. That's exactly the same thing but at a
| different (better) level.
| gravypod wrote:
| There's a bug difference between extremely complex c++
| templates and std::unique_ptr, std::string_view, and
| constexpr. Also, I've heard many game devs still saying
| unit tests either take too long to write or they aren't
| helpful.
| pjmlp wrote:
| For the corpse it makes little difference how it got hit.
| aliswe wrote:
| its amazing to read such a contrarian viewpoint, i dont
| agree but its somehow fresh to read
| maccard wrote:
| > Put another way, I'd rather fix relatively simple C
| (which also tends to be simpler code in general) than the
| monsters created by "modern C++" because they thought the
| "added safety" would mean they could go crazy with the
| complexity without adding bugs.
|
| It's completely possible to write C++ code without it
| being a mess of a template mostrosity and massively
| overloaded function names. People who write C++ like that
| would write C filled with macros, void pointers and all
| the other footguns that C encourages you to use instead.
|
| I've been working with the sentry-native SDK recently [0]
| which is a C api. It's full of macros, unclear ownership
| of pointers (in their callback, _you_ must manually free
| the random pointer, using the right free method for their
| type, which isn't type checked), custom functions for
| working with their types (sentry_free,
| sentry_free_envelope), opaque data types (everythign is a
| sentry_value_t created by a custom function - to access
| the data you have to call the right function not just
| access the member, and this is a runtime check).
|
| See [1] (their C api example). With function overloading
| and class methods it would be much more readable.
|
| [0] https://github.com/getsentry/sentry-native [1]
| https://github.com/getsentry/sentry-
| native/blob/master/examp...
| everyone wrote:
| I'm a game dev and imo web-devs are the 'seat-of-our-pants'
| guys. In games we tend to use compiled and statically typed
| languages. They're pretty strict in what they allow and
| many errors are picked up by the IDE and/or prevent your
| code from even compiling.
|
| Whereas javascript.. Your code could be doing almost
| _anything_ yet it will run just fine. Also, cus its not
| compiled the IDE for javascript is much much less helpful
| (eg. theres no "find all references") and much more
| permissive.
| vvanders wrote:
| I think it depends on a lot on the studio and culture. I
| don't think I saw a single unit tests before I left
| gamedev(a few smoketests to make sure the game didn't
| crash but that was about it).
|
| I also rebuilt our audio streaming system over the course
| of 48 hours to use the texture streaming subsystem when
| we exceeded the 64 file handle limit on an certain
| platform. We needed to hit a date for a TGS demo and I
| can guarantee you that we had things which were even more
| YOLO for a fairly decently sized team/game.
| christophilus wrote:
| To be honest, the game industry is a good counterpoint to
| TDD zealotry. You can go quite far with adequate results
| without a single unit test.
| gravypod wrote:
| Some of the most buggy software I interface with are
| games. They crash and break in strange ways. I often
| wonder what it would be like if someone tested some edge
| cases or enabled a fuzzer for some functions. Like "what
| happens if I kill a freed entity", or "what happens if my
| character is 50% in a wall", etc.
|
| Some of these bugs are experience ruining: think Fallout
| 76.
| oreally wrote:
| And on the flip side tech gets Leetcode interviews,
| shoehorned microservices when you dont need it, slow web
| browsers.
|
| The game industry iterates far faster and the result are
| programs that can handle far more features than the average
| tech methodology. It's the classic quantity leads to
| quality pottery grading experiment. Have you ever
| considered that these 'best practices' pile on so much
| unneeded crap that an experienced developer doesn't need?
| kaba0 wrote:
| I would not necessarily say that game development has
| better quality than web browsers. And the latter are
| anything but slow -- they are engineering marvels no
| matter what you think of them. It's just that websites
| like to utilize it shittily.
| oreally wrote:
| There's a lot of games out there, way more than there are
| web browsers. For a start try comparing games with
| manpower/dev support levels similar to a web browser like
| chrome. If we take a AAA open world game, the game
| somehow gets more features done compared to chrome.
| There's something that can be learnt there.
|
| Also last I heard there was a startup aiming to solve
| slow browsers by running chrome in a server and streaming
| a video of the window somewhere. If that's not a setback
| I don't know what is.
| kaba0 wrote:
| > There's a lot of games out there, way more than there
| are web browsers.
|
| Maybe this should tell you something about the relative
| complexity of the two problems. And frankly, features in
| a game are non-comparable to browser features.
| leetcrew wrote:
| there's a lot of overlap between safety and correctness.
| taneq wrote:
| Yeah man bring back peek() and poke() haha live life on the
| edge!
| TwoBit wrote:
| You forgot to add /s to the end of your comment.
| duped wrote:
| I for one appreciate fewer programs segfaulting due to
| unexpected input, the security is just a bonus
| Koshkin wrote:
| > _you can use float* in C++ too_
|
| But then why bother with C++ at all, right?
|
| > _more compact and readable_
|
| This: overloading of operators (and function) is one of the
| biggest draws of C++ (along with RAII and templates).
| socialdemocrat wrote:
| People get blinded by features. At the end of the day, what
| influence your ability to write safe code is the human brain.
| You have to be able to reason about and understand the code
| effectively to discover and fix problems.
|
| Ironically C++ by adding a ton of features to make the language
| "safer" basically achieves the opposite. Complexity obscures
| how your code works and thus aids in hiding critical bugs.
|
| I have had so many cases with obscure bugs the 15 years I used
| C++ that simply would be impossible to reproduce in C.
|
| Does it mean I advocate C for safer coding? Not at all, but I
| think the advantage of C++ is grossly overstated.
|
| Languages such as Go are better examples for writing safe code.
| Why? Because they quite strong type safety and good memory
| semantics while not adding too high complexity to the language.
| Everything is far more explicit than in C++ which has too much
| "magic" and implicit behavior.
| [deleted]
| kaba0 wrote:
| C++ can introduce bugs by obscuring the flow of code, but it
| can also alleviate many instances by having proper
| abstraction power. It's about balance.
|
| I would say both C and Go falls into the "most lines are
| readable, but the whole is not" camp, due to not enough
| abstraction. For smaller programs one can hold in memory it
| may be good, but it doesn't scale.
| dasb wrote:
| Can someone tell me what it means that "float is unsafe"? I'd
| never heard about that.
| nly wrote:
| The * has been erased by HN's markup.
| maccard wrote:
| It's actually float* - which is a pointer to a float (hn's
| formatting can eat these sometimes)
|
| example: float* f = GetF(); // In a
| C world, you rely on the documentation to tell you how long f
| is valid for. SomeFunc(*f); // We
| _know_ this is safe. std::unique_ptr<float> f =
| GetF(); SomeFunc(*f.get());
| bionade24 wrote:
| The statement was made about a float pointer. Still float
| precision is a problem beginners run into, too. So maybe
| it'll will help one reading it somewhen.
|
| float a = 0.1f * 0.1f; assert((a - 0.01f) < 0.0001); <--
| Works assert(a == 0.01f); <-- Will fail
| [deleted]
| suby wrote:
| >So what is the bare minimum? To use the GPU, I need a graphics
| API. Vulkan is the one that is most widely supported so that is a
| natural choice
|
| Vulkan can't possibly be the most widely supported graphics API
| can it? I have to imagine that targeting older hardware is better
| with OpenGL because it's been around forever, but I don't
| actually know and a quick google search doesn't turn up much.
| pjmlp wrote:
| It isn't, it is also not available on game consoles (Swift
| supports it, but also has GL 4.6 and NVN), or any Android
| device lower than 10.
|
| Even with 10 or later, if not using Google or Samsung flagship
| devices, you will bump into driver issues.
| nly wrote:
| There are lots of Windows machines around with subpar or buggy
| OpenGL implementations.
|
| I used to develop a Qt/QML app for Windows it was impractical
| to leave hardware acceleration enabled. In then end, using
| Mesa/llvmpipe everywhere was just more reliable.
| AussieWog93 wrote:
| You're right.
|
| I did some graphics work in my last job. OpenGL2 is supported
| by just about every device made in the last 15 years, on every
| major operating system, including software OpenGL in VMs.
|
| Vulkan is not officially supported on OSX (although there is an
| unofficial port that is quite good, from what I understand) and
| only runs on modern hardware.
| badsectoracula wrote:
| And by "modern" we really mean released the last few years.
| For example it doesn't run on my 2013 laptop (has a 660M GPU,
| Nvidia supports Vulkan only from 7xx series) or my (late)
| 2016 GPD Win (technically there is Vulkan 1.0 support under
| Linux but not under Windows and AFAIK even the Linux driver
| has issues).
| bsder wrote:
| The MoltenVK guys originally got their start creating a
| commercial offering of OpenGL on top of Metal so that you
| could skip the Apple OpenGL system.
|
| Yeah, that's how crap Apple support for OpenGL is.
| ninepoints wrote:
| > Vulkan is not officially supported on OSX
|
| And neither is OpenGL
| Wowfunhappy wrote:
| Deprecated, not unsupported.
|
| And, like, Apple ported OpenGL to Apple Silicon macs. I
| suspect it's going to stick around, it's just never, ever
| going to get updated.
| 1024core wrote:
| I've always been impressed by Go's compile times. The first few
| times I thought I'd made a mistake, and it was doing nothing (the
| prompt came back so quickly). Now I know better :-)
| christophilus wrote:
| This is my favorite OCaml and Go feature, and one that I wish
| all other languages would adopt.
| qalmakka wrote:
| Yesterday I finished a complex library in C++20, which I wrote
| "Rust style" by following the ISO C++ guidelines. I used 0 `new`,
| no smart pointers, all by-value returns, move semantics and STL
| containers. It literally took a month to write, but it worked as
| expected at the second attempt (first failed due to forgetting a
| `}` in a format string). I did not see a single segmentation
| fault and it works fine on both Linux and Windows. Rust and
| modern C++ give you the expressivity to describe what you want to
| accomplish to the compiler.
|
| You can use the type system to erase certain classes of error
| entirely, without ever having to worry about something pointing
| to bad memory or whatever. If you follow certain rules, C++ can
| be _almost_ as safe as Rust.
|
| I love C personally, and I've been coding in it for more than a
| decade now. It is simple, easy to learn, but it gives you
| literally zero ways to create abstractions. That could be a good
| thing, until you realize that almost all complex C codebases
| reimplement (badly) half of the STL due to libc providing no
| containers of sort. Linked lists are terrible for performance,
| yet C programs are pestered with them due to how simple they are
| to implement. And I won't get started with C strings, which are
| just a historical BadIdea(tm).
|
| Most C codebases tend naturally to evolve into a mishmash of
| reinventing the wheel and ugly macro magic. After all it's either
| that or to risk committing preventable mistakes all over.
|
| That's not without saying that C++ is that better - every three
| years a new release comes out and fixes certain issues so vastly
| that it completely changes how you are supposed to write code
| (see string_view, or concepts). That's not without saying that
| the language is also full of old cruft no one should ever think
| about using.
| Zababa wrote:
| How are the compile times? That's a really big point in the
| article and you don't mention them at all.
| MuffinFlavored wrote:
| > And I won't get started with C strings, which are just a
| historical BadIdea(tm).
|
| What's the better alternative to null-prefixed strings you are
| referring to, length prefixed?
| andreidd wrote:
| Is the library open source? If so, would you share a link?
| qalmakka wrote:
| Sorry but the source is closed sadly, so I can't share it. I
| would like to write a blog post someday about some of the
| patterns I've used though, I think they could be useful to
| someone.
| Santosh83 wrote:
| Would you mind sharing a link to the ISO C++ guidelines you
| mention?
| detaro wrote:
| presumably
| https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
| qalmakka wrote:
| That's correct. Also see https://github.com/microsoft/GSL,
| which is a header-only C++ library that implements the
| Guidelines Support Library as specified by ISO C++. It's
| extremely useful (especially to get std::span<T> if you
| don't have C++20 support) because it provides several
| constructs to explicitly clarify the _intent_of your code,
| like `gsl::not_null<T _> `, `gsl::narrow_cast<T>()` or
| `gsl::owner<T_>`. `gsl::finally()` is also pretty useful.
| phendrenad2 wrote:
| Often the C vs C++ debate comes down to a religious war where the
| C side is arguing that their hammer is best for pounding in nails
| and the C++ side is arguing that their screwdriver is best for
| screwing in screws. I don't believe they're suited for the same
| purposes at all.
|
| If you're writing code where you need to interface with the
| hardware directly (embedded), or manipulate things in kernel-
| space efficiently (OpenGL, audio, games), then C makes a lot of
| sense (and if you try to write C++, you'll end up using C anyway,
| and your attempts to shoehorn in C++ features will be
| ineffective).
|
| If you're writing a high-performance C++ version of a Python web
| service, C++'s nicer memory management and string handling will
| be very VERY useful, not to mention object types for
| serializing/deserializing data.
| kaba0 wrote:
| I don't think this argument holds up. You can create very good
| abstractions over the lower level libs, like OpenGL. There is
| no need to call it "naked" like in C.
| refactor_master wrote:
| Meanwhile, a data science project in python runs right up until
| it doesn't, and the only help you have is heuristics for why your
| data maybe was bad.
|
| I almost sort of envy compilation at times.
| _wldu wrote:
| I used C++ for a good while (systems programming). I like it and
| still use it for some things. But when I started using Go, I used
| C++ less and less. Go was simple like C and almost as fast.
|
| There is a time and a place for all languages, but if I could
| only have one, I'd probably pick C++. It can do almost anything
| in whatever programming style you prefer. It really is the most
| generic language I've ever used. That's both its strength and
| weakness.
|
| Also, the fact that C++ is standardized (ISO) and not controlled
| by a company (Google, Microsoft, Apple) I feel it offers greater
| freedom. No vendor lock-in.
| ncmncm wrote:
| Just sad.
|
| If the language is not doing a great deal of your work for you,
| then you are not using it right. Doing it right, things flow fast
| and work right the first time. So, you don't care that it takes
| 30 seconds to compile, because you coded all afternoon and then
| compiled and it worked right away, with no debugging needed.
| After that, coding C feels like a big PITA where you have to tell
| the compiler everything over and over again, and need to compile
| every few minutes just not to get too far into the weeds.
|
| Any big system has some objecty bits, but O-O is a technique, not
| a religion. Same for anything-oriented: functional, data-
| oriented, what-have-you. Master the tools, don't be mastered by
| them.
| syockit wrote:
| The compiler cannot catch logic or design errors. If you made a
| mistake when designing the algorithm and implemented it as it
| is, you'd need to debug anyway. Sometimes it's something as
| simple as missing a minus symbol somewhere in your equations.
|
| Working with Eigen was especially painful. All the template
| magic it uses means what would have taken 1 second to compile
| if used BLAS/LAPACK, now takes 10-20 seconds.
| jcelerier wrote:
| > The compiler cannot catch logic or design errors. If you
| made a mistake when designing the algorithm and implemented
| it as it is, you'd need to debug anyway
|
| But that's the thing, it can.
|
| - you can specify compile-time preconditions and use the type
| system to ensure value are in range (which catches a ton of
| errors) - you can use constexpr to enforce no UB (UB in
| constexpr evaluation is a compile error)
|
| For instance, it's trivial to make a type-checked db id in
| c++ which will give you a hard error if you use the id
| pertaining to a given type, to another type. That's much
| harder to do in C (without implementing a custom code
| generator at least that would basically reimplement
| templates) and has saved me lots of very costly hours of
| debugging when I see the amount of time I got compile errors
| I got from that.
| patrick451 wrote:
| How does the type system catch this bug
|
| double negate(double x){ return x; }
|
| ?
| jcelerier wrote:
| I'm sure that in 1986, before standardizing ANSI C,
| someone victoriously typed the same thing in front of his
| tty in a discussion about using types in declarations and
| not just doing it a la K&R with just the function name
| being called cowboy-style.
|
| Just because a system does not catches _all_ bugs does
| not make it useless - for me, even if C++ 's type system
| had caught only one bug it'd be worth it (and in most of
| my code, which is mainly about fairly specific domain
| objects, the compiler saves me from myself pretty much
| daily).
|
| In any case, I'd likely have this somewhere
| constexpr double negate(double x) { return
| -x; } // example implementation
| for the sake of the comment, there are much more
| exhaustive libs out there constexpr bool
| sample(auto func) { bool ok = true;
| double x = -1000.; while(x < 1000.)
| { ok &= func(x); x += 1.;
| } return ok; }
| static_assert(sample([] (double x) { return negate(x) ==
| -x; }));
|
| this way I'd get a compile error if my cat walks on my
| keyboard and inadvertently removes the - (also a
| relatively common occurence)
| patrick451 wrote:
| You're effectively just changing where you write your
| unit tests. So it's hard to agree that the type system is
| saving your from anything, when it's really just writing
| tests.
| jcelerier wrote:
| But no one said that type systems disqualifies from
| writing tests (and C++'s improvements over C aren't only
| about the type system).
|
| It removes, however, the need for a lot of tests that are
| needed when using weaker type systems, mainly because you
| can make invalid data unrepresentable.
|
| To give a few examples:
|
| 1/ in C++ you can write a type positive_int such that
| invalid values (negative ints) are not representable at
| all - either the operation a - b gives a correct,
| positive result (3 - 2 == 1), or you get an error that
| you can process if for instance you do 2 - 3. You can
| also write things in a way that the user only has to
| provide things in a declarative way.
|
| 2/ I'm working on an API for defining dataflow nodes for
| multimedia applications which tries to leave zero margin
| for error : the user simply has to define the inputs and
| outputs like this, as types, which the C++ type machinery
| is able to digest and show to the user :
|
| https://github.com/jcelerier/score-simple-
| api-2/blob/main/Si...
|
| In contrast, in C, one has to cast things left and right
| because the only tool you have is void* : see for
| instance
|
| https://cycling74.com/sdk/max-
| sdk-7.3.3/html/chapter_msgatta...
|
| or
|
| https://cycling74.com/sdk/max-
| sdk-7.3.3/html/chapter_msp_adv...
|
| in both cases we are defining a dynamic dataflow node
| with various inputs / outputs (for instance an audio
| input and output, plus a control), which is then
| displayed in an user interface (thus things have to be
| reflected in some way). But in the C version, the onus is
| on the programmer to :
|
| - cast the inputs / outputs to the correct types
| according to the object's specification when using them -
| cast the values contained in the inputs / outputs to the
| correct types - access the correct inputs / outputs in
| the arrays
|
| there is just so much more opportunity for error, which
| entirely disappears in C++
|
| 3/ Another example that you can see in my code: the
| make_uuid function. Takes a UUID in string form and
| converts it to binary uint8_t[16] at compile time ; also
| checks that the UUID is a valid one.
|
| Every time I use this function, this is a test that I do
| not have to write - I _know_ that all UUIDs in my system
| are valid, because I use an "uuid" type which can only
| be created this way by the user.
| pjmlp wrote:
| By using contracts, unfortunately they were postponed to
| C++23.
| adrianN wrote:
| Naturally you first implement your algorithm together
| with a correctness proof inside the turing-complete type
| system. After that writing the real implementation can be
| left as an exercise for the compiler :)
| patrick451 wrote:
| Clearly, if you missed a minus sign somewhere, your type
| system is not powerful enough (>:<)
| b0tzzzzzzman wrote:
| Why are all the things I post dead?
| thrower123 wrote:
| It seems like most of the issues with C++ are solved by not using
| the parts that suck. You can still write C++ as C-with-
| classes/RAII/operator overloads, and leave all the smart pointers
| and template meta-programming and boost (do people still use
| boost?) magic at the door.
| thechao wrote:
| I like to think of a compiler as a virtual machine whose
| instructions are the tokens in your code, and whose output is
| object code. Taken that way, you can _for sure_ optimize the
| input program (your C++) to execute faster on the interpreter
| (the compiler). Understanding how the language (any language with
| such features) is implemented lets you stray away from the slow
| parts.
|
| For instance, in C, every function has a unique, global name.
| That means the implementation in the compiler can be a hash-table
| from string to implementation. C++ allows function overloading;
| now, our 'hash table' is a much more complicated thing: first, we
| must consider every function in the overload set no matter what;
| second, we must pick which function is "best" -- usually through
| some complicated unification scheme. In all, this means that C
| function lookup is O(1) (probabilistic), whereas C++ function
| lookup is O(n) _at best_ , but with unification it might be
| O(n^2) or O(n^3). I've seen slow compiling code with _hundreds_
| of identically named functions.
|
| Templates are slow for two reasons: (1) they get shared via
| header files, so they get reparsed over-and-over-and-over; and,
| (2) they're implemented with a stringy interpreter whose job is
| to instantiate copies of the code. This is distinctly slower than
| macro expansion; generally, macro instantiation is of _code_ and
| so occurs in source files -- only requiring a single parse; and,
| also, there 's just string-replacement, not an interpreter.
|
| But ... here's the thing. You can make your C++ code compile fast
| by simply _not using slow paths_ through the compiler.
| woodruffw wrote:
| > For instance, in C, every function has a unique, global name.
| That means the implementation in the compiler can be a hash-
| table from string to implementation.
|
| If only we were so lucky ;). Symbols are not unique across the
| translation unit boundary, which is why we have C's mess of
| fake namespace hacks. Even `static` doesn't save the linker
| from doing extra work --- check out the locally bound symbols
| in one of your ELFs sometime! You'll probably find some
| duplicates.
| WalterBright wrote:
| > I've seen slow compiling code with hundreds of identically
| named functions.
|
| I sometimes think I've seen everything programmers do, and then
| something like this pops up.
|
| > You can make your C++ code compile fast by simply not using
| slow paths through the compiler.
|
| D is fast to compile because it sidesteps or redesigns features
| that make for slow compilation.
| fm77 wrote:
| Walter, I am sorry, completely off-topic... but I have to ask
| - and you don't have to answer ;-) ...is that you?
|
| https://archive.org/details/byte-
| magazine-1988-09/page/n148/...
| Zababa wrote:
| There's a (french) wikipedia page about Zortech
| https://fr.wikipedia.org/wiki/Zortech
| thechao wrote:
| If I told you the first attempt was several _million_
| identically named functions, split across several hundred
| files, and the compile and link times were ~72 hours on a
| distributed build would you be happier?
|
| A single templated function & a few n*100kloc macros later,
| and I got the compile time down to ~1s on a crappy laptop.
|
| Macros are 'fast paths' through compilers -- they copy
| structures in memory, rather than going through the entire
| disk->lex->parse chain.
| moonchild wrote:
| D is horribly slow to compile template-heavy code for exactly
| the same reasons as c++. And it encourages lots of
| monomorphization in e.g. ranges. The enhanced introspection
| and reflection (and CTFE) capabilities coupled with mixins
| also encourage code that is very slow to compile.
| WalterBright wrote:
| Not really. For example, D templates are never parsed more
| than once.
|
| The trouble is, people use templates more and more until
| they hit compile speed problems. I'd argue that this limit
| is much higher in D, but the end result is similar.
| munificent wrote:
| Just like nature abhors a vacuum, my experience is that
| regardless of how fast your compiler is, user programs
| will grow to until they reach a point where they are
| annoyingly slow to compile.
| mpweiher wrote:
| Solution: drastically lower your annoyance threshold.
| You'll be more grumpy but much more productive.
|
| I get antsy when my test suite takes more than a second
| or so.
| mhh__ wrote:
| As opposed to encouraging manually written code which is
| also not free to compile but has the added benefit of
| potentially introducing bugs?
| moonchild wrote:
| As opposed to encouraging polymorphic code (a la haskell,
| java), which is fast to compile and provides better type
| safety.
|
| (Yes, I know neither javac nor ghci are particularly
| fast, but that doesn't detract from the general point.)
| zozbot234 wrote:
| > You can make your C++ code compile fast by simply _not using
| slow paths_ through the compiler.
|
| Rust is also working on making this easier, BTW. There's still
| quite a bit of excess monomorphization going on when using
| generic code, but better support for const: in generics should
| ultimately obviate this.
| jcelerier wrote:
| I wonder what kind of hardware OP runs on, and which compiler is
| used.
|
| Here on my laptop (Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz), on
| linux, compiling an eigen example takes 0.7 seconds
| clang++ -fuse-ld=lld eigen.cpp -I/usr/include/eigen3/ 0,66s user
| 0,07s system 100% cpu 0,728 total
|
| if I put the eigen header in a PCH this drops to 0.1 seconds
| clang++ -fuse-ld=lld eigen.cpp -I/usr/include/eigen3/ -include-
| pch 0,08s user 0,02s system 104% cpu 0,099 total
|
| I have a hard time seeing how 300 lines of code (vs the 10 lines
| of the example) would multiply compile times by 300 - here's the
| example I used #include <iostream>
| #include <Eigen/Dense> using Eigen::MatrixXd;
| int main() { MatrixXd m(2,2); m(0,0)
| = 3; m(1,0) = 2.5; m(0,1) = -1;
| m(1,1) = m(1,0) + m(0,1); std::cout << m << std::endl;
| }
|
| I develop a GUI software with boost, Qt, and a fair amount of TMP
| and my average turn-around time from a change in the average file
| to the program running is between in and 2 seconds. Maybe it
| could get down to 0.1~ seconds if using C, but there would be so
| many less checks and guards !
| tomn wrote:
| Unfortunately our example isn't representative, and avoids a
| lot of things that make eigen slow to work with. Parsing the
| header file will take a constant time, but template expansion
| etc. can take a long time if you have more code.
|
| Try adding a bunch of matrix operations (particularly chained
| ones resulting in massive expression templates), fixed size
| matrices of various sizes, and compile with optimisation and
| SIMD enabled, and you'll see different results. I really like
| eigen and use it a lot, but it can definitely result in long
| compile times.
| dthul wrote:
| Does it get significantly slower if you use Eigen's fixed size
| matrices? I have a C++ project which uses Eigen and some other
| templates (but nothing crazy) and it is quite slow to compile.
| Not complaining though because I don't compile often.
| yoz-y wrote:
| Haven't used Eigen for quite a while, but I do remember it
| being rather slow to compile once it proliferates. Those
| templates are clever but also quite taxing.
| pjmlp wrote:
| Even so, using another math library would have been the
| solution then.
| tomn wrote:
| The bits of eigen that make it slow to compile are the same
| bits that make it both easy to use and fast. I'm sure there
| are improvements that could be made, but ultimately eigen
| is big and slow to compile for a reason.
| socialdemocrat wrote:
| I can relate a lot to this as an old school C++ developer (not
| anymore). I also remember switching a project over to pure C some
| years ago and found it a lot more pleasant to work with. But I
| combined it with Lua so I could use Lua for high level constructs
| C is not so good at.
|
| Anyway I suspect that in a few years Zig will be the choice in
| scenarios like this. It gives you fast compilation times and low
| level access like C while being a lot safer, but without adding
| C++ level complexity.
|
| That is an important sweet spot to hit.
| winrid wrote:
| If you really like the simplicity of C I highly recommend Nim:
|
| * Compiles to C, so you stand on the shoulders of giants wrt
| compilers.
|
| * Very low overhead GC that doesn't "stop the world".
|
| I was working on a project recently where I had to just write a
| bunch of stuff to disk, and I tried Node, and then Java, and they
| were about the same, and I figured - yep, makes sense, it's IO
| bound.
|
| Nim got twice the throughput w/ an order of magnitude less memory
| usage! Because the serialization I suppose was the bottleneck and
| msgpack4nim is pretty fast.
| moonchild wrote:
| If serialization performance is an issue, then you may want to
| consider zero-overhead serialization tools like capn proto.
| winrid wrote:
| Yeah I know about Cap'n proto etc. Was just saying the tiny
| language can compete :)
|
| Language comparisons are almost always BS...
| jart wrote:
| Use mmap and you won't need to write anything to disk; the
| operating system will do it for you. That's C's killer feature
| while also being the last thing on earth languages like Node
| and Java would ever permit developers to do.
| shakow wrote:
| > while also being the last thing on earth languages like
| Node and Java would ever permit developers to do.
|
| You sure about that?
|
| https://www.npmjs.com/package/mmap-io
|
| https://docs.oracle.com/en/java/javase/11/docs/api/java.base.
| ..
|
| mmap is a system call, not some C-exclusive fancy feature. If
| your language can print something on the screen, there is no
| reason it can't mmap a file.
|
| And if the serialization was the bottleneck, mmap would not
| have changed anything anyway.
| winrid wrote:
| Good idea, will keep that in mind for next time.
| nmeofthestate wrote:
| >linking a big C++ project with lots of dependencies and some
| template magic can easily take several minutes.
|
| _dreams wistfully of C++ build that only takes several minutes_
___________________________________________________________________
(page generated 2021-06-26 23:03 UTC)