Hacker News new | past | comments | ask | show | jobs | submit login
Modern C++ Programming Course (github.com/federico-busato)
493 points by asicsp on Nov 28, 2023 | hide | past | favorite | 194 comments



FWIW, the creator of this course is a team lead at NVIDIA, and principal software engineer of CUDA cuSPARSE.


With the context of learning Modern C++, and the author's association with nVidia and CUDA, I share this anecdote: Partially if it helps anyone directly, and partially as an opportunity for someone experienced with C++ to poke holes at it.

I've had great success using ChatGPTv4 to help me write modern C++ that works with CUDA, using prompts like these. (I have not attempted to get it to write parallelized code given loops/serial code as an input):

> Please convert this Rust code to modern C++, compatible with the latest version of the nvcc CUDA compiler: ```rust ```

It will output a compilable/working result, and explain each of the language differences in question. It will make sure to use `std::*` instead of the more rigid C-derived equivalents when able, and take advantage of looping over array pointers etc, vice i++ loops. `auto` var types etc.


Any ideas where to start learning C++ as an embedded developer? I wrote many lines of bare metal C code and want to transit to higher level jobs. I see many expensive or completely free courses, but I am not sure which one is usable in my complicated situation.


It depends on what you target. I do alot of Kernel and really low level C++, and it's a completely different style than even app dev, so you have to be sure you don't go down the completely wrong route.

For my target space, there's a few rules for c++.

Footguns: A. No STL, it causes to much bloat when binary size is critical. B. (almost) No inheritance or virtual functions. Again, it causes some bloat and adds in some instructions and code to support these features that can cause issues in low level environments.

Things to learn (All work in the kernel):

A. Learn templates, especially c++20. Templates add ZERO bloat, they literally just generate the function again with proper types the same way a macro would.

B. Use RAII.

C. Use constructors and overloading.

D. Zero pointers should be exposed to code except where absolutely needed. If you need to pass by reference, use the c++ style.

E. All allocations should be wrapped in a class with RAII. You can overload the operators on the class to make it seem like a pointer so you can still do if(!RAIIPointer).

A good video on kernel c++ is here https://www.youtube.com/watch?v=AsSMKL5vaXw . You can use a surprising amount of C++ language features in the kernel and it makes it extremely smooth and safe if you do it properly.


I would add constexpr (I guess it might be included with templates?).

You can do some nifty stuff like generate CRC lookup tables at compile time without hardcoding them.


Yes! 100% Right, can't believe I left that out. One of the big things is to make sure to use ConstEval if you can do a new verson of c++(20), since ConstExpr isn't guaranteed and can leave stuff in the binary that can hurt size or obfuscation. For example one thing I see alot is lookup tables for hashing who use constexpr and hide some key data in there, but it "may" just randomly do it, and can cause alot of issues.


How does the use of ConstEval address this issue?


> No STL, it causes to much bloat when binary size is critical

> A. Learn templates, especially c++20. Templates add ZERO bloat, they literally just generate the function again with proper types

"Avoid STL due to bloat but embrace templates" doesn't make sense to me. Isn't that exactly what code bloat is? Too much code generation?


Bloat can also refer to the invisible infrastructure code that's compiled into a binary when using certain language features. C has a fairly linear relationship to the size of the compiled assembly, iirc, but C++ will vary wildly based on which features you use.


Indeed, it is a common refrain among C++ programmers to inspect ones binaries, I have found ..


I think you missed exceptions often being a problem in low level and embedded targets. That knocks out most of STL anyway.

I also think you are a bit harsh on virtual functions - it introduces a single indirection, yes, but sometimes that is fully justified. RTTI on the other hand... of course depends a bit on target characteristics.

Perhaps controversial, I've also found (especially bare) lambdas and even std::function objects useful, although may evolve into something purely static when the dust settled. Highly dependent on target of course.

It will be interesting to see the final form of the new MISRA std, since the active one predates all of this.


If you can't use the STL because of exceptions: https://www.etlcpp.com/


Sure, there are more embedded friendly libraries, but that wasn't the topic.


It’s almost as if this is some sort of discussion board where people can expand on the topic as they see fit :D


You can use quite a lot of STL even with -fno-exceptions. Things that you probably need to ditch are:

std::vector

std::list

std::map

std::unordered_map

std::set

std::unordered_set

std::sort

std::transform

std::merge

std::string

std::regex

std::swap

std::function

std::stable_partition

But there are much more than that in STL.


most (useful) containers are out, what else are left in STL for embedded systems, algorithms?


True: "most" was a stretch.


newest MISRA for c++ just came out, though it's based on c++17


If you want to understand the C++ in terms of C code then probably you would need to understand the C++ object model. I mean, how C++ classes are translated into C equivalent code. If you are interested in these you can take a look at a tutorial I wrote long back https://www.avabodh.com/cxxin/cxx.html

If you want to write bare metal C++ then this page from the above tutorial will be useful https://www.avabodh.com/cxxin/nostdlib.html


Well, you seem to be precisely the target audience for the course linked in this very post:

    > This open-access course is directed at those who are already familiar with C 
    > and object-oriented programming towards a proficiency level of C++ 
    > programming.


Maybe, in this context it makes lots of sense, if you know C well there are things which "just work how you expect" in C++ and so they need at most a brief refresher.

I agree with Kate Gregory that this is a bad way to teach C++ from scratch. That is, if you know only say, Python or Java (or nothing) and want to learn C++, you should not begin by learning C. Kate's approach skips many of the things you can do in C++ but you shouldn't, whereas if we try to start with C you're going to see that a lot, because "they were legal in C" is why they're allowed in C++ even though they're a bad idea and then you have to unlearn things. You're maybe expecting some of that if you've years of C programming experience, but there's no need to introduce an idea on Monday only to tell students on Tuesday that it's a bad idea and they must never do that.

The most insidious cases are where something is legal and correct in C but it's wrong in C++ yet it will compile and you maybe have broken software. There are some nasty type punning examples, where the pun was an idiomatic C expression which does exactly what you wanted, but in C++ that's Undefined Behaviour and all bets are off. You'd just never teach those type puns in a pure C++ course, they're never the Right Thing™.


There is a considerable divergence of opinion on that subject. In my view, C++ isn't remotely suitable as a programming language for someone without a healthy understanding of C. Perhaps a dialect of C++ could be developed that was more of a cousin to Rust, but C++ as we know it is a C like programming language with a very large number of features added and all the ways to fail just like a C program does. There are real world advantages of course, but it is not a language for the faint of heart, not even close.


> C++ isn't remotely suitable as a programming language for someone without a healthy understanding of C.

I always look askance at folks who say they know C++ but not C. The C++ abstract machine is built over the C abstract machine and it becomes even more clearer when you go down to the binary level.


Depends what you mean by "understand C". As you say, understanding the C abstract machine and memory model is critical for a C++ programmer.

Understanding C idioms, the standard library, best practices, and general C software architecture , is less important if not downright negative early in your formation. You will end up picking a lot up anyway if you stick to C++ long enough.


> is less important if not downright negative

"Less important" maybe but not "negative".

You have to have a decent C base (not necessarily expert level with knowledge of dark corners/tricky idioms/etc.) before you start with C++. One example is being comfortable with raw pointers. I often see "Modern C++" proponents say you should never use (and by inference learn) raw pointers which is absolutely counter-productive. It is also easier to learn C++ as a "better C" in the beginning else it becomes overwhelming. I believe this is the main reason most beginning C++ programmers find the language "scary/difficult/huge/overwhelming/confusing". They are trying to learn everything at the same time which is an impossibility with a language as baroque as C++.


The main use of raw pointers in practical standard C++ is that they still don't have an analogue of Option<&T> so raw pointers (which can be null) let you write analogous code albeit in a less friendly way.

But this isn't so much like C, where raw pointers often express ownership.

You aren't going to really learn "everything" in C++ anyway, regardless of how you approach it, the language is a vast sprawling mess, the fact somebody wrote a serious book just about initialization [https://leanpub.com/cppinitbook] in C++ gestures at the problem. Freeing themselves of the need to have a language which is well-defined, or which can be demonstrated to be sound, or really follow any principles whatsoever was doubtless briefly convenient but the result is unmaintainable nonsense.


The use of raw pointers in C++ which you deem analogous to a Rust feature is your view and not that of the vast majority of C++ programmers. Raw pointers in C++ are the same as in C and the usage techniques are up to the programmer.

As a diehard proponent of Rust your views on C++ are well known and there is nothing new here. But the fact of the matter is that the industry runs on C/C++ and the main reason is due to its baroque set of features whatever one may think of them.


By "negative" idioms I'm mostly referring to stdio, string.h (except for memcpy/memmove of course), goto-based cleanup, void* based generics, overuse of macros and a certain fondness for global mutable state in a lot of classic C codebases.


Disagree here. Given the language's limitations, everyone of the idioms you list has its place and uses. They are just a way of structuring code for different abstractions.


In C maybe, but not C++.


That's not what i was saying (it's a given). After all C++ was invented to provide programmers with a large number of features that would allow them to express abstractions naturally and directly rather than building them up with all the basic plumbing that you would need to do in C.

However there is a huge amount of C code and programmers who would like to move to C++ and it is for them that the techniques of C coexisting with C++ are very relevant. There can be no wholesale rewrite of the code into C++ but a gradual rewrite module by module and as needed. Using C++ as a "better C" is the path here.


Can you maybe share some paid/unpaid resources to learn C++ from scratch for someone coming from Java and JS?


Kate has a PluralSight course which I have watched (not really to learn C++ but to see her approach) and that seemed decent to me, although of course I can't vouch for it having taught what you'd need coming from Java and JS, not least because my background is far more polyglot including Scheme and the Standard ML for example.

I definitely think Bjarne Stroustrup's book about his own language isn't very good, even in later editions (which correspond to a more modern language) and so I would avoid buying that, I'm not sure it's a good defence of the language as an idea and I'm sure it isn't an effective tutorial, even if not for being out-of-date.


That doesn't address the constraints that are typical in embedded systems such as limited memory (RAM) and the expectation that a program will run forever.

There are resources oriented toward embedded C++ that address these (at least memory allocation.)

On a project a couple years ago I was pushing to use C++ instead of C, if only as a "C compiled using the C++ compiler" for better type checking. I could not convince the project lead, who's technical depth far exceeded mine, that the C++ compiler was as likely as the C compiler to produce correct translations.


I wouldn't worry about the compiler producing correct code, I would worry about the heap eventually fragmenting enough that an allocation fails (perhaps thousands of hours after the program launches). I know C++ offers the ability to supply your own allocator, but it's not for the faint of heart.


I will go through the course before Xmas. The thing is that in bare metal development even C standard library is not always being used. Recently I wrote some string processing functions for very specific parsing task. Nobody would need such crap while working with operating system.


1) Real-Time C++ by Christopher Kormanyos. - For using C++ in embedded systems.

2) Software Architecture with C++ by Adrian Ostrowski and Piotr Gaczkowski. - Gives the overall picture of how to use C++ plus relevant modern tools as a complete development system.

3) The C++ Programming Language by Bjarne Stroustrup. - The bible/reference with programming techniques for the language.

Learning C++ is not difficult and don't let the size of the language intimidate you. Survey the different ways of programming in C++ i.e. Imperative/Object-Oriented/Generic/Meta-programming and use them as needed in your project without trying to master everything; that will only happen over time as you gain more experience.


You can definitely start putting C++ into your embedded projects, and get familiar with things in an environment in which you're already operating. A lot of great C++ code can be found with motivated use of, for example, the platformio tooling, such that you can see for yourself some existing C++ In Embedded scenarios.

In general, also, I have found that it is wise to learn C++ socially - i.e. participate in Open Source projects, as you learn/study/contribute/assist other C++ developers, on a semi-regular basis.

I've learned a lot about what I would call "decent C++ code" (i.e. shipping to tens of thousands, if not hundreds of thousands of customers) from such projects. I would suggest finding an open source C++ project, aligned with your interests, and study the codebase - as well as the repo history (i.e. gource) - to get a productive, relatively effortless (if the interests align) boost into the subject.

(My particular favourite project is the JUCE Audio library: https://juce.com/ .. one of many hundreds of great projects out there from which one can also glean modern C++ practices..)


This is maybe a very unpopular opinion, but if you go from C to a higher level language, I would advise to steer away from C++, certainly if it's would be your first OO language.


I’d recommend reading Effective C++ books from Scott Meyers and trying to interview for the jobs you’re looking for over taking a course!


If you want to transit to higher level jobs you may get better mileage out of broadening to other languages (e.g., python) or skills (e.g., project management) than from a C to C++ transition. My 2c.


Do you want to do embedded/low level c++, or are you looking to transition out of that?


I will get out. Application development using C++ and Python. Maybe C#.


Anyone went through this and can compare it to https://www.learncpp.com/?


learncpp might be more beginner friendly and more verbose


I picked C++ back after a long break and I have to say in reasonably recent iterations (like C++17) it's good enough, although OOTB Unicode is a PAIN. They should have solved it in C++11 and moved on. It's a disgrace.


Does Carbon or c++next fix the ootb handling?


It's being actively worked on for C++2x. This video is a fairly recent update on how it's progressing: https://www.youtube.com/watch?v=AoLl_ZZqyOk

Note for those who are not used to C++Now talks: they are intented to be a bit more informal with regular interjections from the audience (who at C++Now are also often heavily involved in the development of the C++ standard), so don't think people are being rude by jumping in!


This is just an idea at this point. Ask again in 30 years.


Hopefully it's really good in the near future. https://thephd.dev/cuneicode-and-the-future-of-text-in-c


Just what I needed to brush up my long forgotten from University C skills, but I would prefer practical coding tasks for each part.


> Heap Memory - new, delete Keywords

Should "modern" C++ even use new/delete? I'm a C++ n00b, but I thought these can be avoided entirely now.


I noted that - IMHO one of the big advantages to "modern" c++ is the focus on the lifetime of objects with smart pointers rather than the mechanics of heap allocation and pointers. Pushing that down to chapter 19 as "Advanced Topics" seems like a mistake. Arguably the same with std::array vs object[] - the first should "probably" be the default now for teaching new programmers, so covered first (or at least highlighted when discussing array[]-style variables)

I also note a number of the possibly-ambiguous c-style casts in examples, despite already having mentioned the explicit versions (static_cast and the like).

Plus there seems to be a fair bit of system & machine dependent stuff mentioned like it's /always/ true - like stack being at a "higher" address space than the heap and code sections, or the stack growing down. It also says that the Data section is "slower" than the stack which... isn't true? If they're spilled out of registers, they're likely functionally identical? Or at least for small microbenchmarks depend on specific implementation details, like how the pointer to the object is calculated and dereferenced.... And object size and cache interactions... And a million other things...

And that's just the first few chapters I looked at :P


Smart pointers should really be like, chapters 2-10.

In my experience, every useful C++ application requires wrangling some historic smart pointers implementation: Boost, Unreal, libwebrtc, Qt.

Even NVIDIA uses its own smart pointers (thrust::device_ptr) though that's understandable. Also there's stuff like this: https://stackoverflow.com/questions/65938991/how-to-use-lamb... for e.g. TensorRT.


Yes, in new code you should be able to replace almost all new and delete with unique_ptr, vector or, more rarely, shared_ptr. Placement new can still be useful, but delete not really. There are some patterns where you use new without a delete for a global singleton that is intentionally never deleted, as well.

It's still useful to learn about the concept when dealing with old code.


Not entirely - you can't generally use std::make_unique with private constructors.


It's explained later in the course


I think nowadays smart pointers should not be considered an "advanced topic" in C++. Smart pointers are usually the best way to handle memory management.

It's definitely useful to learn about "new" and "delete", because those are the primitives that memory management is built on top of. But it should be followed up with good advice like, rarely use these in practice. You should almost always be using unique_ptr or shared_ptr instead of new'ing up pointers that you intend to explicitly delete later.


> You should almost always be using unique_ptr or shared_ptr

I really wish there was a thread unsafe version of shared pointers, without atomics overhead, in the standard. Maybe without weak pointers.


If there's one thing C++ needs, it's more slightly-different varieties of smart pointers in the standard. ;-)

What exactly would you use these for? I find that usually I can get away with unique_ptr for the sort of object that I have many of where pointer performance matters.


Some sort of problems where you throw trees around. Almost interpreter like programs, where trees are arbitrarily stored on different places. And where you for some reason don't want a proper GC ...


This course is a bunch of presentation slides. The idea that you can learn anything from slides is rather silly. Learning from slides is almost as bad as learning from random youtube videos.


Slides can be an extremely good medium for learning. Because brevity is required by the format, slides can require a lot more thoughtful attention to emphasize and carefully explain key points than the long-form text format. I think the course looks really nice!


In fact when I read a book I do little cards, like slides, with the most important information condensed. I find a good method.


All channels with reputation at some point were random (except the ones from universities maybe)


YouTube videos are fine, at least they have narration. With slides you're kinda left to infer your own story and lessons.


"A Little History of C 3/3" says that C was used in the special effects for Star Wars. I do not know what relevance this has, honestly, but independent of that is the strange choice of photo: an image from the Empire Strikes Back which shows stop-motion models with optically-composited, rotoscoped lasers. Excepting that this may be from the digitally recomposited Special Editions, no C code was used in the making of this shot.


Watch Lights & Magic on Disney+ and you will understand why the reference is in there. ILM, Pixar, WETA had huge influx on graphical swe... hell even photoshop was a sideproject of ILM at the time.


Isn't the wireframe death star scene the only part done in C (and therefore by computer) in the first movie?

https://cdm.link/2021/11/watch-larry-cuba-explain-how-he-ani...


Yes, done by Ed Catmull himself.


Care to enlighten those of us who don’t already know the explanation?


ILM was and is an absolute software engineering power house and they started with motion controlled cameras for star wars at the time all their in house software was c++. A lot of software which is still used today. Pixars Renderman, Photoshop, Maya... was initially engineered at ILM or Lucasarts which were subsidiaries of Lucasfilm.


Maya was an Alias|Wavefront product, which was a subsidiary of SGI. It came along way after Pixar had been spun out of ILM. John Knoll, who co-developed Photoshop with his brother Thomas, is still at ILM as the CCO.


Thank you. That makes sense


Can someone explain how to read the Conversion Rules section in the second chapter? I haven't seen this style of notation before. For example:

    Implicit type conversion rules, applied in order, before any operation: ⊗: any operation (*, +, /, -, %, etc.)
    (A) Floating point promotion
    floating type ⊗ integer type → floating type
    (B) Implicit integer promotion
    small integral type := any signed/unsigned integral type smaller than int small integral type ⊗ small integral type → int
    (C) Size promotion
    small type ⊗ large type → large type
    (D) Sign promotion 
    signed type ⊗ unsigned type → unsigned type
Edit: oops, I missed the explainer. The ⊗ stands in for any operator in case that was confusing to anyone else who missed it. :-)


Is there a good guide on the toolchain? What do people use today to keep sane? Something like meson, ninja or cmake? I have inherited some project in scons that was generating msvc 14.1 project and I can heartily recommend against that


Meson or CMake. Neither one is really all that great, I would say personally that Meson is better in theory but CMake is more widely supported so I prefer Meson but it's pretty even. Either one is better than just an enormous makefile. Ninja is a different level of the stack, you can use meson+ninja or cmake+ninja.

Either way the C++ dependency management situation even makes the Python dependency management situation look good by comparison.


> Either way the C++ dependency management situation even makes the Python dependency management situation look good by comparison.

Is there a better way than “script the compilation of all dependencies and share the result” for any big project that has to support Windows? I’m interested in alternatives.


At least on Windows I quite like vcpkg for dependencies.


I've been setting up my little docker image that has both llvm/clang/tidy/clangd and gcc inside with boost with an idea I'd run it and from outside use it to code with live checking and compiling without littering my OS. Now, I feel like I'm doing something that already exists but couldn't really find what everyone use, ought of those that would prefer such a setup. Debugger within is probably a pipe dream, but who knows.

Basically I'd prefer to have a docker image with tooling that I can hook into.


I find it absurd that a programming language which compells someone to set up a containerized OS just to manage their build toolchain could be considered "modern".


I install Rust's toolchain and development tools inside of a container, I also do this with Python and will do it with C++ sometime in the future.

I don't do this because I have to, I do this because I prefer to keep non-system -critical software managed by non-root users and separated from the systems rootfs.

On your common desktop Linux distro, I think C and C++ toolchains are the least difficult to setup and use without a container though (for me). On Gentoo I can just emerge gcc or clang and enable whatever USE flags I want, and they are installed and updated automatically with the rest of my system.

I use the Gentoo system package manager to manage my Rust toolchain as well instead of using rustup, so that it behaves like described above, it's updated and managed automatically and with the rest of my system!

I do realize that many distros have issues with software being out of date though, and that is a big problem! With Gentoo I can install multiple versions of most things in parallel and can very easily package anything that doesn't exist yet.

Also to clarify, I use the system package manager to build and manage my containers, this is how I use the system package manager to manage Rust's toolchain but also have it inside of a container. All of my containers are just nested Gentoos that I can install stuff into with the system package manager. I can also install a package manager into the nested Gentoo and build/install stuff while "inside" of it.


> I install Rust's toolchain and development tools inside of a container, I also do this with Python and will do it with C++ sometime in the future.

The thing is, you don't need to do this with Rust as far as I can tell. There may be some benefits, but ultimately your project can easily specify its own compiler version, its own target directory (the default is per-project), etc. There are some shared resources like caches, which you can split if you want to.

I can see why you'd still do this - but, the main reason would be... if you have dependencies on C/C++.


> On your common desktop Linux distro, I think C and C++ toolchains are the least difficult to setup and use without a container though

In my experience, the hard part is rigorously controlling the libraries a build uses. Using CMake, it's easy enough to add libraries to a build, but harder to stop the thing going off and looking round /usr/lib64 and so on. On my physical workstation, there is all sorts of stuff in there, because i have a desktop environment and a cornucopia of tools installed. I don't want a build using any of it! If a build needs a library which i have not explicitly added, i want it to fail, not use something from the system. But between default paths and rpaths in libraries and so on, that seems hard to do in a watertight way. I've done endless fiddling with sysroot flags, but i'm not sure it's not leaking. A container takes care of all that in a very definite way.


I'd be really interested in a write-up of your setup with more details so I can try it.


I cannot disagree. Gcc is trivial to compile and set with prefix however you want. Llvm behemoth, not so very much though and I'd like to have both for reasons. There's always some gotcha involved, and when you finally set it all up, you forgot what and how you did it and then you dig through shell history file to reconstruct what you did in order to replicate it on other machines or same if you nuke the OS.. blah. Rustup and cargo and pyenv and nvm even spoiled us.


It would truly be absurd to claim that C++ is a modern language. A subset of C++ however, is modern. This does not include the toolchain and standard build practices.


Your point stands, but it is/was implied, how I took it at least.


I guess that's why i went into JavaScript instead of java. I use to write HTML back when i was around 10 years old, back then java applets were the only way you could have something interactive and dynamic on your page, so i started to learn it. After a while i learned that you could do a lot of neat stuff with JavaScript. So i stopped using java.


VS Code is designed to work with these - https://code.visualstudio.com/docs/devcontainers/containers


Interesting, thanks for that. That's kind of the idea, but preferably without the 'extend' part on top of containers. I'm not using VSCode though so I'll shop around or maybe I'll convert, who knows.


Maybe nix (https://github.com/NixOS/nix) is a better tool for what you're looking for if you're on Linux, you can setup nix shells and work in them, what's installed inside the shell won't be accessible from outside.


As is with most people and nix, it's on todo list to check it out. This might be the trigger. Thanks!


One thing that would have helped me when I started learning C++ was learning the C++ compilation model i.e. translation units.

It is surprising how many people look surprised (that also claim they know the language) when you tell them that code in .cpp does not get inlined into other .cpp files no matter what doing compilation. (yes the linker can (and should) do that with LTO doing linking)


Compilation units are mainly about symbol visibility and as you already realized at the end of your comment don't have anything to do with inlining as far as the C++ standard is concerned. Neither does the inline keyword btw.

The linker doesn't inline anything, LTO/LTCG is about running (part of) the compile process at link time. But that's really no concern for the C++ code but an implementation detail of the toolchain - you could just as well not have a separate link step and instead have the compiler process all compilation units in one step.


yes, LTO itself does not inline but from a user point of view it does, and that is exactly my point. If you are a beginner and you do not come from C, then you might not think about these things (maybe you do I did not).

I get that it is just an implementation detail, but so are many things in C++ and you usually care about these things otherwise you would not be using the language in the first place.

Many projects also disable exceptions that is also an 'implementation detail' (or at least something that is definitely not required by the standard I would imagine), but now you are technically not writing C++ anymore.

I guess this is also one important thing when learning C++. The community is very fragmented, and there seems to be disconnect between C++ users and committee.


This also helps understanding why template code completely wrecks compiletimes and ram usage since the compiler cant share template instantiations. This becomes very relevant if template metaprogramming is used in bigger projects.


That's not why. Template processing happens during processing of the translation unit, so it would be expensive even if you only had a single TU in your build. It's true that a template has to be reprocessed for each instantiation, however, that's not merely from one TU to the next, but even inside each TU! For every distinct value of T for std::vector<T> in a single TU, the compiler has to process and generate std::vector entirely


What is the C++ job market like? I do mostly Python / web app developmnt, but I don't like the churn.


Having “spent some time in the job market lately”, the salaries seem astronomical if you’re C++ and fintech/HFT. I think some funny business is going on with those listings though…

Kinda average in game development and sadly pretty low in embedded systems.

All way less common than your average Python position, but probably way more interesting.


I'm not sure if you're in this field specifically, but since you mentioned it I'll ask. Is it your impression that fintech and HFT shops would possibly start to transition to Rust?

I've always heard that they're currently mostly using C++ (and sometimes Java). But I'm not sure how dogmatic or change-averse they are on average.


If you are strong in C++ you can get pretty good jobs in robotics, trading, game engines. But TBH at that point it might be more interesting to learn Rust that is on the rise across the industry.


Nobody (except hobbyists and maybe an indie or two) is using Rust for game development, and it's unlikely they ever will. The cost of switching far outweighs any benefits in an industry where memory safety is not critical.

C++ is here to stay for a very long time.


Treyarch gave a GDC talk in 2019 that they were using it for tooling, Embark hasn't shipped a game in Rust yet but is larger than an indie. You are absolutely right that it is small right now, but "unlikely they ever will" is a more open question, IMHO.

Memory safety is absolutely important in certain kinds of games. At least, as a player, I want games to not be exploited for cheats, and I don't want progress lost when a game crashes due to memory issues.

All that said, I would agree that if you want a job in games, learning C++ is a better idea than Rust right now.


No not strong, just considering making the switch.


C++ was my weapon of choice in college mostly because writing graphics stuff in C required re-inventing the wheel a lot of the time for basic data structures and the STL was pretty slick.

Having spent the better part of the last decade writing go though, I think C++'s syntax is a bit too cluttered.


If you prefer to read a book rather than read slides: http://www.icce.rug.nl/documents/cplusplus/


I get 404d on that page. For others: you can download it (in various formats) through here: https://fbb-git.gitlab.io/cppannotations/

(You might have to jump through some links)

Also, fancy seeing someone else from RuG here :)


Just read the first pdf. This is an interesting quote.

"Every second spent trying to understand the language is one not spent understanding the problem"

Did a lot of work in LaTeX the last few years and they make C++ looks like something made in heaven


I did not enjoy the compile-edit-debug cycle when I was using C++. Have there been any advances in the area off a C++ REPL?


This brings back horrible memories at uni.


How many interesting SE jobs use C++ anymore?

Is anyone using it for new projects?


Compilers , OSs, browsers, VMs, Databases, scientific experiments, high performance systems, HPC, video games, complex desktop applications, simulations.

In fact the question should be: what interesting projects don't use C++?

For some very subjective definition of intersecting of course.


This is obviously very subjective, but C++ is used in many interesting fields: robotics, graphics, signal/image processing, physics/game engine, ...

I'm currently doing some backend work, and I really miss it. I don't think I've done anything interesting algorithm-wise since I switched. Alas, the pay is usually much better in SaaS land.


What's your definition of interesting?


When they say modern, which standard to they mean?


Literally in the title of the linked page:

> Modern C++ Programming Course (C++11/14/17/20)


It's described in the course, but canonically this means at least c++11 (and these days, likely 14/17 in practice - there is broad toolchain support)


Do we have a curated list of similar programming courses for other programming languages?


looks good,is there one pdf for all chapters?


I have cloned the repository then combined the chapters using pdftk:

     pdftk *.pdf cat output Modern_Cpp_Programming.pdf


This worked on the Mac. I had to install pdftk (brew install pdftk-java) first. The result is nicely formatted for reading on an iPad after importing into Books.


on airplane with a phone so can't do that,wish it had a download link for the full PDF,will create when I am back to my pc


Termux?


> Table of Context

Table of Contents?


It is of type <T>.


I like to watch Formula 1 and NASCAR races. As interesting as seeing the cars pushing the limit of materials and mechanics is just as interesting seeing them crash. It shows the flip side of design. I wish this sunny-side tutorials had at the end of their books some black pages with the hall of horrors. Weird compilation messages and pointers getting demoted and random segfaults in real scenarios. We all remember the inverted wings in the F1 cars and vehicles literally taking off when the body loose close contact to the ground. Now in NASCAR the roof flaps come off when the car gets slight bump and the front wheels loose traction. C++ Is not a risk averse language.


I’m quite fond of the C++ frequently Questioned Answers: https://yosefk.com/c++fqa/


"Note: some parts of the FQA are not up to date with C++11/14/17. You can contribute to the FQA on GitHub."

LOL, yeah I think we can stop spending time on this (which was quite funny back in the day).


There are at least a dozen C++ intros that go through every little detail like this and I don't get it. I have a pretty good ability to retain a lot of information presented this way, and I've been programming in various C-like languages for long enough that much of this isn't new to me, but this doesn't seem like a good way to learn the material. I'd much rather work through actual programs and iterate on them. I imagine that a lot of people who would be looking for something this basic aren't even going to retain most of this nor understand why they would need most of these things they are memorizing.


I agree with your grievance: Many c++ tutorials are basically like: these are the internal workings of how the graphite in a pencil is structured, now go draw the rest of the owl.

I am not saying that this isn't important, but learning about pointers and references is totally useless unless someone shows you why you would use them, where you would use them etc.


I think Kate Gregory makes similar good points about the structure of C++ learning materials here:

https://www.youtube.com/watch?v=YnWhqhNdYyk


I think C++ and Rust are honestly special beasts. I've heard experienced programmers say that you want to actually pick up a book to learn Rust, and the same is true for C++.

Like you can basically hack your way through learning Python or JS. I didn't learn Python from a book, for sure.

But with C++ and Rust that's not an optimal strategy. You have to do both -- do practical projects, and actually study it a bit.

There's also a huge amount of disagreement about C++, so I think it makes sense to start with a codebase you want to work on, and then ask the people who WROTE that codebase what C++ books and learning materials they like.

e.g. Game C++ is kind of different than Google C++, but both are good (they get work done, and multiple people can work together in that style). There is a lot of really bad C++ out there. The gulf between good and bad C++ is bigger than good and bad Python or JS. You want to learn from experienced people who get things done, not language lawyers


I virtually always read a book to learn a new language. My point is that memorizing every language feature as a starting point is not a great way to learn, but almost every C and C++ book/tutorial/course seems to do this. There is a reason why people love K&R's C book.


I’m curious, if you were going to learn a general purpose lower level language today what would you go with?


perhaps zig... the new cool kid on town

or pascal.


[flagged]


Rust has a number of opinions that may not fit how you want to work. If you want to do a shared library rust will fight you. If you want to break your project up into multiple repositories rust will fight you. Rust will fight you if you don't want to use cargo as your build/package system. You can make it work, but the above are things that may or may not be acceptable costs to you.


It depends on what your goals are. If you pick Rust for your product be ready to roll up your sleeves and contribute to the crates you are depending on. IMO your investment is worth it because developing in Rust is a delight and when it works it will be very solid, but the ecosystem isn't as mature yet.

If you want to focus on building a product and get something out there relatively quickly, C++ has a huge amount of ready-to-use libraries, but you're on your own with making sure to run sanitizers and making any sense of object lifetime and thread safety. You might ship an MVP faster but I wouldn't want to maintain it after that IMO.

Personally I've made peace with the Rust ecosystem being not-quite-there because I can see that many things are about to get to the point of being really solid. There's a lot of hard-working folks volunteering their time to build out great libraries, and I'm hopeful that the 2024 edition is going to be a huge improvement for async ergonomics.


Does it make sense to learn Latin when Chinese, Hindi, English, and whatever else exists? It all depends on what you want to do with your life.


I think you have it backwards: Latin/C++ should be considered legacy while {Chinese, Hindi, English, Rust} still have a future.


> Does it make sense to still invest time to learn C++ when Rust exists?

Yes. The overwhelming majority of industry is using C/C++.

Rust is still very much niche when compared to the overall market.

Additionally, (Someone can correct me if this is wrong) IIRC, mozilla, was initially one of the biggest proponents of rust, but still just uses rust in a supplementary role. C++ still powers the heavy lifting.


Mozilla is not “one of the biggest proponents of Rust” anymore, in that other, larger entities are now as well. Rust is at the center of many products, large and small.

That doesn’t mean that C and C++ aren’t also large, even larger, but this has been changing for years and only shows signs of accelerating, not slowing down.


The size difference is like Sutter Buttes versus Mahalangur Himal, so unless it's accelerating at the speed of light, C++ will be dwarfing rust's usage for many years to come.

This is the umpteenth time large tech companies have sponsored C++'s successor. Go was famously created to eventually supplant C++ at Google (or more specifically, solve the issue of long build times). Over a decade later, C++ is very much still in heavy usage at Google.

If I were a bettin' man (or if I were trying to increase my general employment odds), and the choices were between Rust or C++, I would pick C++.

That being said, if I were in a comfortable financial position, wanted to scratch a nerd itch, and have fun programming again, I would absolutely pick Rust over C++.

But those are two very different scenarios.


I certainly do not disagree that C and C++ will be in general usage for a long time, if not forever. However, because the industry is still growing, the proportions change, even as the total number grows in size. C and C++ do not have to die, or even shrink, to become a smaller part of the overall pie, and Rust does not need them to outright die in order to grow and thrive.

I agree with you that this is not the first time languages have tried to encroach on the last bastions of the spaces where C and C++ have not yet been ousted as the default choice, but Rust is actually gaining traction in production use-cases in those spaces, unlike many of those languages.


Thank you!


It really depends on your career goals and whether you're doing greenfield or maintenance coding. If you want to be part of the "new generation" of tech, learn Rust. If you want job security, dont care about doing anything glamorous or exciting, and don't mind a long learning journey, go with C++. It will always be around and it's hard enough to learn that you won't have as much competition.


Only things written in Rust I know are rewrites of decades old C applications or rewrites of js tooling. What "new generation" of games, browsers, editors, CAD applications, etc. are written in Rust? Is Rust used in aerospace, automotive, healthcare, instrumentation or finance industries? What is glamorous in Rust that hasn't been done in other languages?


I think rewrites can be as "glamorous" as greenfield, because you get to use a modern language and toolset. yes you have some drudgery tacked on, which is likely having to wade through legacy C code, but having a perfect set of working requirements (do what the old app does!) more than makes up for it.


It is difficult to answer this question because there are so many answers, and they're so broad. I will try to give some partial answers.

> games

Some indie games, a new AAA studio has been working on a game for a while (Embark, their first game is still in C++ but they are building the foundations for the next ones, which takes time), at least one AAA studio known to use it for tooling (Treyarch). The first time I personally gave a talk at a AAA studio about Rust was 2019, though I don't believe that studio is using it for anything I am aware of.

> browsers

Firefox is about 12% Rust by volume, but is also only 41% C and C++, so it's about a quarter of the relevant systems level code. Chromium has Rust in the tree, but only libraries right now, they don't write new code yet except wrappers. Brave has an adblock component in Rust.

> CAD applications

Not aware of movement here.

> aerospace,

Very early days, some small projects, nothing massive yet.

> automotive,

This one is gearing up massively: a rust compiler was just qualified for the relevant safety standards to be used in automotive, several large manufacturers have had job openings open mentioning Rust. It certainly hasn't taken over the world yet but there's a lot of actual movement in this space.

> healthcare,

Not aware of specific things here.

> instrumentation

I don't know what the "instrumentation industry" is.

> finance

Some players are using some things, but as a very proprietary industry, not a lot of specifics are known. I have given an internal talk at a big hedge fund a few years back, unsure if they're doing anything in production just yet.

There are other big successes that aren't covered by these industries; Shopify adding a JIT to Ruby, Cloudflare's heavy use of Rust (which means that a significant chunk of internet traffic passes through Rust code), AWS's heavy use of Rust (the core of products like Lambda and S3 are in Rust these days), Apple using it for network services appparently, Meta keeping their monorepo in a version control system written in Rust, and it being an official language to use on projects there, Google using Rust for significant components in Android, Windows having added Rust code already, with more to come, Rust becoming the next language used in the Linux kernel... the stories are endless at this point.


Basically it is being used as any other language out there. Nothing more glamorous than what existing languages are used for.


I don't know why this gets downvoted so much. It seems like a good newbie question.


Agreed, as a Python dev who tried a bit of Rust a while ago, I was wondering exactly the same thing. Bored of the churn and fads with higher level stuff.


[flagged]


Genuinely asking:

Why many programmers need tools to enforce something upon them? Why not they can't take slow, be mindful about what they write and add a couple of layers as pre-commit hooks? Like a code formatter and maybe a linter?

This makes me sad. Programmers have the knowledge base to make some of the most sophisticated things bend to their will, and they tell a programming language is bad, because they can make mistakes with it. You can cut yourself while chopping onions, too, if you're not careful.

When I code C++, I generally select the structures/patterns I gonna use, use them, and refactor my code regularly to polish it. Nobody enforces this on me, but this is how I operate. I add a formatter to catch/fix the parts I botched on the formatting department, and regularly pass my code through valgrind to see whether it makes anything funny on the memory department.


> Why many programmers need tools to enforce something upon them? Why not they can't take slow, be mindful about what they write and add a couple of layers as pre-commit hooks? Like a code formatter and maybe a linter?

Why would I want to? Most of that is boring stuff that's hard to get consistently right and highly amenable to automation. Automation is what the computer is good at, it can have that job.

Like, why would I want to spend more mental bandwidth on tracking down whether every new goes with every delete than strictly necessary? Yeah, once in a while something is highly performance sensitive and it pays to design it just right for the use case. But it still tends to be surrounded with hundreds of other things that will do just fine with a smart pointer.


> Automation is what the computer is good at, it can have that job.

Pre-commit hook is automation. It formats the code and gives you linting notes the moment you write "git commit".

> Like, why would I want to spend more mental bandwidth on tracking down whether every new goes with every delete than strictly necessary?

It's not more mental bandwidth for me. Because I write the new, and directly delete at the point I need to, then I never think about it again. If I botch something, valgrind will tell me, even pinpointing it with line number.

> ...other things that will do just fine with a smart pointer.

If that works for you, use it. I have no objections.


> Pre-commit hook is automation. It formats the code and gives you linting notes the moment you write "git commit".

So isn't that a tool that enforces something upon the programmer?

> If I botch something, valgrind will tell me, even pinpointing it with line number.

What is the benefit of needing valgrind? Isn't it even better to have the task automated away so that the problem doesn't even come up?


> So isn't that a tool that enforces something upon the programmer?

It's a tool I voluntarily add to my workflow, which works the way I want, when I want, according to project needs. It's not a limitation put upon me by the language/compiler/whatnot.

> What is the benefit of needing valgrind? Isn't it even better to have the task automated away so that the problem doesn't even come up?

In most cases, performance (think of HPC levels of performance). If I don't need that kind of performance, I can just use the stack or smart pointers.

If we're talking about moving to other programming languages, I'd rather not.


> It's a tool I voluntarily add to my workflow, which works the way I want, when I want, according to project needs. It's not a limitation put upon me by the language/compiler/whatnot.

I spent a lot of time maintaining a lot of Perl code. While it was generally very well written, it also made me a big fan of strict, demanding compilers. Perl is quick to write, but can have very high debugging costs since it'll let you get away with a lot that it shouldn't like having a function called with arguments but that completely forgets to retrieve them.

Based on my experience, IMO any class of error that can be eliminated, should be.

So my modern approach is -Wall, -Werrors, -Wextra, and anything else that can be had to that effect, all the time.


> It's not more mental bandwidth for me

Most people are not you. Which, I guess, also answers your original question of "Why many programmers [do the things not they way I would do]?". Incidentally, this is also the root of all the complexity in managing and coordinating people.


So use RAII and smart pointers. I think that's the point.


Yes, exactly what I'm saying. Use tooling to do the boring parts of your job.


It’s not tooling, it’s the language.


Languages are not tools?


Every programmer knows the difference the language and the tools surrounding it. This seems like you're trying to create a pointless semantic argument for no reason.


First of all: I am failable. I do make mistakes, even if I concentrate. Secondly I want to verify code others wrote. If a tool does the first pass quickly and automatically, I can quickly ensure some basic level of compliance and can focus on the relevant part.

In the end it boils down to: Let the computer do what a computer does and do the things a computer can't.

Doesn't say, one shouldn't think, but computers are there and are powerful, so use it. If the checker doesn't find anything: great. If it does: good it's there.


the problem is of course that tools aren't particularly smart. When you create heavy handed restrictions in your language, you're not just eliminating mistakes, you're eliminating tons of potential programs that make perfect sense, that's to say you drastically reduce the expressiveness of a language.

That's why Rust say, has an escape hatch. Unsafe Rust wouldn't exist if all you could write in it were mistakes. Async Rust is to put it plainly, a pain in the ass.

These high level tools are more like chemotherapy. You hope that they kill more of the bad code before they kill you. They're not sophisticated, and it's fairly reasonable to prefer a language that let's you opt-in to stricter safety rather than opt out.


The biggest problem with C++ isn't that you can't write clean, structured code with it.

It's that the language is so vast that the odds of any two developers working on two different projects agreeing on what that means are low. I programmed in C++ for a decade, then for a year or two at a second place, then picked it up again in a third... And all three places had nearly completely different best practice protocols. Pre-processor macros are banned, but deep template abstraction and CRTP abounds. Interface and implementation are separated even when templates demand they be in the same header chain, but here we do so by squirreling away the implementation in a separate file with a non-standard suffix instead of splitting it out but keeping it in the bottom of the same file. In my previous company, we used pointers to indicate the called function could mutate the data... In my new firm, pointers in interfaces are just about banned unless absolutely necessary and we indicate whether data could be mutated via const and documentation.

The language is broad enough to let the developer change almost anything, but it is unfortunately broad enough to let competent developers build competing and equivalently (un-)safe styles.


Embedded shop? MISRA? Sounds strangely familiar...


Because not everyone has the same values, not everyone has an engineering background even though they like to call themselves engineers, most programming projects are delivered by external companies that don't care about quality unless it is required by law (aka liabilities).


Look at it this way: every hour, people are coming across C++ for the first time. You can’t expect them to have the same discipline as seasoned programmers. The thing is that even if you teach them “the way”, there’s always a new batch who is clueless. You’re never going to get rid of them, and even the experts are going to make mistakes and take shortcuts.

Better to have sane strict defaults with an escape hatch for experts rather than an open range filled with footguns for any newbie to pick up.


Because even the most careful C++ programmer still makes plenty of mistakes. Not the, "oops I cut my finger," kind. Those are usually found by linters and analyzers. More like, "there's a PCI breach and now we're liable for millions of dollars," kind: the semantics of C++ are hard to nail down and ensuring that private data doesn't leak to other threads is almost impossible to get right with only knowing C++ and being careful.

You generally need to use higher-level tools for that kind of work: Coq + the Iris framework, for example, in order to prove that your system can freely share memory and not leak secrets so long as X, Y, and Z hold, etc.

Or you need to run a ton of tools like Jepsen to find if your system behaves the way you think it does.

What baking more of the specification language into the programming language does (ie: better type systems) is enable us to have more of these guarantees earlier in the development process when changes are cheaper and faster to make (at the expense of requiring more expertise to use them).


> Why many programmers need tools to enforce something upon them? Why not they can't take slow, be mindful about what they write and add a couple of layers as pre-commit hooks? Like a code formatter and maybe a linter?

Running a formatter and linter in a pre-commit hook is literally using tools to enforce things?


I have noted elsewhere in the thread, but I think I was unable to express myself very clearly.

What I really meant is enforced externally on the programmer, in the form of compiler, development environment setup (from elsewhere) or other guidelines, without any free will to tune or disable them.

The layers I add are voluntary, just checks and balances I decided to add myself because I think they help me, and not enforced as part of the language or toolchain I'm forced to use.

IOW, a self-respecting developer striving to do a good job can continuously sharpen themselves iteratively, tuning their processes and fixing the problems they see as they go along their journey.

Perhaps pjmlp understood the gist of my comment, and his answer is a pretty nice hit on the head of the subject. Honestly, I'm coming from a point where programming is more of a passion which pays rather than work/job for me, hence I have an inner drive to do my best and improve continuously, and not everyone shares the same set of values or inner drive about programming, and want to be railroaded into an environment where they can do the absolute minimum to get things done.


I agree with you. If those tool actually enforced good quality it would be one thing but what is actually being enforced is mediocrity and enforcing the power play of some individuals who have decided for all others what is good.


The fact that we now have languages enforcing safety makes them more desirable than a language that doesn't.

If I'm writing a small toy project where I'm the only person messing with the code, I can totally do it in C, or even raw assembly if I feel like it.

If anyone else will ever write to the same codebase, or the code is for a client/company, I want every automated static check I can possibly have.


That is all well and good on a small team of senior people , but if your project has more than a handful, with mixed experience levels, you want tools to enforce a minimum standard before even getting to code review


Software development for at least the last 20 years has been about cranking out code.


You need to talk to Bjarne and Herb...

"C++ Core Guidelines" - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines


> Why many programmers need tools to enforce something upon them?

You can always count on the threads about C or C++ to have somebody ask questions like this.

As a hint, you won't see it asked on any other context. The answer is "all", no exceptions. It's widely known. For decades. In fact, C wasn't even universally used when people discovered this.


Most things learned don’t provide strict tools to enforce that you don’t use old practices where better modern practices exist. Do you question what the purpose is of learning most things?

Also, there are tools to look for old practices and suggest modern ones.


There is no modern C++ without the old C++. C++ is the success that it is exactly because it has been able to evolve with no shameful past to cancel.


We don't talk about auto_ptr.


`union` is all I need to mark my territory and make sure no one touches my property!


This is still the language that supports setjmp and longjmp and just documents that if you mix them with exceptions the behavior is undefined, right?

You can't have a shameful past when you started shameful. ;) This language's roots are in "I wrote some extensions to simplify C, but I don't want to make it incompatible with C so the extensions don't work coherently in all contexts and you only get sound code if you hold your mouth right" and it never actually got better because nothing ever got removed to make the language more sound.

Well, it did. When it happened, it created other languages.


I've had the misfortune of needing to fix the C++ from someone who neither knew nor cared what STL and smart pointers are.


I'm still trying to clean up the mess from someone who knew what STL and smart pointers were, and then made their own broken smart pointers (their version of shared_ptr has a particularly nasty bug).


You mean someone who learnt Visual C++ from Microsoft and was conned to think he knew C++?


Visual Studio will yell at you if your code isn't conforming to the "C++ Core Guidelines" (those guidelines basically define what "Modern C++" even means).

Unfortunately it also yells at you when your *C* code violates the C++ Core Guidelines (at least it was a few years ago when I permamently switched that "feature" off).


Many of the "Core Guidelines" are semantic requirements which are (provably) Undecidable, so even if tooling was created for them the tooling would necessarily have either false positives or false negatives (those are the only options, unless "both" counts as another option). In practice most of these are unaddressed, Microsoft understandably focused on checks which are never wrong and give actionable advice.

"Guideline support" does include libraries with classes that e.g. provide a slice which raises an exception on bounds miss, which is something, and it's certainly notable that the equivalently named C++ standard library feature is a foot gun† whereas the one in the guideline support library is not, so that's a good reason to adopt this. But VS does not in fact ensure you obey all the guidelines, it's very much a "best effort" approach even with that switched on.

† WG21 (The "C++ Standards Committee") seems to have concluded that because sometimes the fast way to do something is unsafe, therefore the unsafe way to do something must be faster... as if car executives noticed that 240mph crashes in their flagship sports car were often fatal and concluded that if they re-design their under-performing SUV so that the fuel tank explodes during any collision killing everybody aboard that'd improve its top speed...


> tooling would necessarily have either false positives or false negatives (those are the only options, unless "both" counts as another option).

Getting stuck in an infinite loop and never producing an answer is a 4th possibility for attempted solutions at deciding undecidable problems.


Good point. Note that we can't necessarily tell it's an infinite loop, the constructive proof for this Undecidability reduces it to our old friend the Halting Problem, so we're in the land of the Busy Beaver.


It still does, given Microsoft's stance on security, and that C++ used to be the main systems language, hence why C support languished until pressure from relevant customers to pick it up.

I say used to be, given the recent announcement of Azure business unit to switch to Rust as the favoured systems language for new engineering activities.


> C++ Core Guidelines" (those guidelines basically define what "Modern C++" even means)

No, it defines what Microsoft C++ means and nothing more.


Looks comprehensive. I’ll take a look.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: