Hacker News new | past | comments | ask | show | jobs | submit login
Learning Standard C++ as a New Language (1999) (drdobbs.com)
64 points by goranmoomin on March 11, 2022 | hide | past | favorite | 36 comments



Somewhat related: [1]. A bit provocative title, but it's actually about "teaching (the newcomers) C++ constructs first before going through C-era ones".

[1] CppCon 2015: Kate Gregory “Stop Teaching C" (https://www.youtube.com/watch?v=YnWhqhNdYyk)


Right, there have been a few other CppCon talks about this, but the thrust is the same. Kate is an excellent speaker and so hers is a good introduction to the idea if you're (somehow) still of the opinion that the first week of a C++ course ought to be C and she has great motivating examples where doing this her way works out better for everybody. If C++ is in fact this good modern programming language, your C++ programming course should teach that language. If your excuse as to why the programming your students do is lousy is "Well, they didn't learn modern C++" that's your fault, you were supposed to teach that.

I actually think there are also important lessons to be learned when trying to teach any programming language as a first language, because beginners are likely to offer some illuminating questions, and that begins with the boilerplate (the Dobbs article calls it "scaffolding" but I think boilerplate is appropriate).

The boilerplate in C++ is very bad. I am convinced that it would be less bad (maybe still pretty bad, but less bad than say C) if right early on somebody had sat down with students learning this as a first language and noted down all the warts they run into and set about fixing as many of those warts as possible.


C++ still mixes with lots of C code especially in the system programming field, i.e. C++ has to use many Posix C calls, C++ does not have its own socket() API replacement for network programming, even C++ thread uses pthread underneath, etc.

Unlike other languages, C++ needs quite some C code to get the whole thing working sometimes. I do agree that when you learn C++, you should not focus on its C roots.


> C++ does not have its own socket() API replacement for network programming

In experimental but there is the Networking TS: https://en.cppreference.com/w/cpp/experimental/networking

> even C++ thread uses pthread underneath

https://en.cppreference.com/w/cpp/thread was added in C++11. How it is implemented doesn't really matter, does it? It doesn't leak any pthread-ness.


That's rather an inane observation. A program written in any language running on a system with a C API will eventually need to call to C through some kind of foreign function interface in order to interact with the system. C++ just has the peculiarity that its FFI is directly interoperable with C, so much so that it's legal C.


This is an interesting observation, but this also means people will often be expected to call those APIs directly as not everything will warrant a C++ wrapper. In other languages, with their (typically) noisy FFIs, almost no app. programmers use them directly (or at least very sparingly), but instead use a library wrapper written by someone else in their language. I think the point of the OP is that it creates a very unique "blended" experience in C++ vs. other languages and I would agree.


The advantage of a wrapper, even when the underlying API call is simple, is that your program can be system-agnostic. You don't need to care at the call site whether the call eventually resolves to pthread_create() or CreateThread(). The implementation has already taken care of it.


That's not nearly the advantage you think it is. For many of these APIs, it's going to be just as easy to have a portable implementation of the pthread's or socket API itself. Simply wrapping an API, but otherwise exposing it as-is (which basically everyone does for threads & sockets) does not gain you any portability whatsoever.


A bit harsh to call it inane observation. Going by the original example, the challenge is the lack of good c++ libraries for common tasks in systems programming (like Sockets, or networking - still experimental after 40+ years!!) and the only option is to directly embed C code into your C++ code. Other languages will keep the distinction between your C code and the host code. Python for ex. might have C code behind the scenes, however you will interact with the C library in a pythonic way. Your mental mode of your python program (regarding lifetime/memory of objects) is consistent.

In contrast, with C++ your program has Hodge podge of high-level constructs like std::vector, string, RAII etc.. and suddenly you are in the void*, malloc, free land with your C code. The mental model of lifetime/memory of objects in your program is inconsistent and burdensome. People would rather program everything in C, at least it will be consistent albeit very low level.


> the lack of good c++ libraries for common tasks in systems programming (like Sockets, or networking - still experimental after 40+ years!!)

I don't think it's that shocking, is it? In most languages the networking support is basically just the C socket API exposed as well. A good API is a good API, it shouldn't really matter how it's implemented.

Also seems like a massively unfounded leap to go from "the standard library doesn't have it" to "complete lack of good C++ libraries for common tasks". C++ has never been a "batteries included" language. That doesn't mean good libraries don't exist for it. Boost is very common in the C++ world, for example. And that "experimental" networking library is basically just asio but adopted by the standard library, and asio is not experimental or unstable: https://think-async.com/Asio/

> Python for ex. might have C code behind the scenes, however you will interact with the C library in a pythonic way

Kinda odd to use Python as an example when it just exposes C sockets pretty much directly and entirely non-pythonicly https://docs.python.org/3/library/socket.html

> In contrast, with C++ your program has Hodge podge of high-level constructs like std::vector, string, RAII etc.. and suddenly you are in the void*, malloc, free land with your C code. The mental model of lifetime/memory of objects in your program is inconsistent and burdensome. People would rather program everything in C, at least it will be consistent albeit very low level.

Eh? No, people would just make a tiny wrapper around the C APIs and call it a day. It's a pretty odd stance to take to say that to avoid writing ~100 lines of C-style C++, you should instead write 10k lines of C?

Also other than void* (which is in C++, too), you shouldn't be encountering malloc or free when calling C functions. That's not how any good C API works, and it's certainly not how the continuously mentioned socket APIs work.


Note that I was mainly responding to the comment about std::thread using pthread internally. Not only is that not necessarily true, as the platform may not even have pthread, in those cases where it is true it's also true of the implementations of all other languages. How does Java do threads on UNIX other than by sooner or later calling pthread?

I totally agree that sockets is a long overdue oversight in the language.


> the challenge is the lack of good c++ libraries for common tasks in systems programming (like Sockets, or networking - still experimental after 40+ years!!)

C++ has plenty of excellent libraries for such tasks (just think of boost), they are just not part of the standard library.


Why would C++ need to reimplement libraries and other features when it already has access to the ones in C? If it was supposed to be an entirely separate language then they probably would have called it D (pretty sure C++ was a thing before the actual D language).


I never understood how you couldn't focus on its "C roots", which are coincidentally also its core semantics (give or take). Why shouldn't one focus on that?

I could agree that C++ has many constructs that allow one to get away without slightly advanced programming that would be needed when coding in C, but recommending that one shouldn't need to understand function is a bit much.


Pays mighty well too!


This is from 1999 and should not be recommended today, as C++11 was such a huge improvement over C++98/C++03.


Some of the specifics, sure, but the premise is still sound: Teach C++ as Standard C++, not C in disguise, or C(++). It would certainly have cleaned up a lot of my former colleagues' code if that had been the case for them (to their credit, many of them also inherited the C(++) code and didn't just make from-scratch C(++) code, but they also didn't take too much time to fix it).


The specific examples in Bjarne presents aren't affected that much by C++11. But generally you're right, in that one should rely on 1999 materials for learning C++11 - nor 2011 materials for learning C++20 :-)


[Disclaimer: I only read the first part.] Let's say we include in the C standard library a routine, `const char *scan_str(int fd)`, one only need to remember to free the returned string once finished with it. Or we can even get away by using static buffers. All the safety and practical requirement are merely implementation details that "user" needn't care (unless they are the implementor). It appears to me that most of the complexity argument in the article is on the availability of library routines, plus, there is a shift of roles from implementor to user -- we all wear different hats from time to time. The article was from 1999, but similar arguments are still popular today or even more so, that using convenience of library availability as strength in language. A rich standard library plus some implicit notations to using the standard library may have its appeal to some users. For some other, who understands the possibility of million ways of implementing a function and the importance of understanding subtleties, C++ is really too complex to use.


I think using std::string and so on from the get-go may be sensible if C++ is the language being used for an introduction to programming, in order to not get mired in difficult details. The great complexity of the language stemming from its history makes it not a great language for beginners, though!

On the other hand, if you are trying to teach C++ to people who already know how to program, then you want them to be able to understand existing C++ code, not just write new code. So they need to understand the complex parts of the language and why they are like that, and the easiest way to do that is to start where C++ started, i.e. with C.


> So they need to understand the complex parts of the language and why they are like that, and the easiest way to do that is to start where C++ started, i.e. with C.

Unless you're talking a very old & not well maintained codebase, there's not really any benefit to starting with C if your goal is to learn C++. It's probably more harmful than anything, even, as it'll teach you things that are bad form in C++. Like using malloc & free - even 'new' and 'delete' are poor form in modern C++, it's all std::make_unique or std::make_shared.


Totally agree. Coming to C++ nowadays must seem a bit odd compared to other statically typed OO languages (e.g. Java, C# or similar). For oldtimers like myself that learned coding before C++ it's a natural evolution and I can accept C++'s awkwardness as the price to pay for a multi-paradigm language that can still be used as a portable assembler (with some manual labor).

I remember the Dr Dobbs article from when it came out and I have to agree that I was still using all the old stdio.h, string.h, etc. functions. To this day streams, which was probably the first thing C programmers had to swallow, seem "unnatural", whatever that might be.

For newcomers to C++, now as well as in 1999 when the article was written, you have to really take the time to appreciate what C++ is trying to do. I switch between C#, C++ and Python and C++ programming has a certain feel to it that I think you have to learn (or be forced to) appreciate.


You can see how ugly Bjarne's streams are even in this small article, ignore for a moment the strange fact that the Dobbs article uses the wrong operator on the last line.

    cout << "number of elements = " << buf.size()
         << ", median = " << median << ", mean = "
         >> mean >> '\n';
This is clearly improved by just having string formatting (a feature C manages to have, but here's Rust's)

  println!("number of elements = {}, median = {median}, mean = {mean}", buf.size());
It's OK though, twenty years later C++ managed to get as far as

  std::cout << std::format("number of elements = {}, median = {}, mean = {}", buf.size(), median, mean) << std::endl;


> a feature C manages to have, but here's Rust's

This is clearly disingenuous. C's string formatting is a completely different thing from that Rust snippet or your snide remark about std::format.

I'd rather use printf than cout, I think iostream is clunky, but printf is definitely a footgun & a fairly common source of bugs itself. So much so that compilers had to add support for specifically recognizing printf() calls & validating the inputs.

Also don't forget C still doesn't have standard placeholders for sized types. Gotta use that derpy PRId64 & friends which makes printf() almost as ugly looking as iostreams.


The C standard could insist they perform print format validation and thus make it safe for string literals. As you note, popular compilers do this today so it would merely be requiring what is in fact common practice.

As published C++ 20 allows a compiler to swallow this nonsense:

  if (untested_condition)
    result = std::format("Oops {} {} {} {}", only, three, parameters);
... and spit out a program that will only blow up when untested_condition becomes true in production. Fortunately sanity prevailed as I understand it, and the revised document correctly requires that this is a compile time error.


That's again a disingenuous phrasing. The compiler didn't swallow any nonsense. std::format is just a function, there's no direct compiler support for this. The {fmt} library did validation at runtime (which still needs to happen if the string isn't a constant!). The C++ working group then pushed to make it a compile time error in the cases it could be as part of promoting {fmt} into the standard library, which {fmt} did on existing C++20 compilers & that improvement has been folded into the spec.


printf() is likewise just a function. If we're not calling out std::format("{}{}",oops); as nonsense then we'd have no right to complain about printf("%s%d",oops) either. I say it is nonsense because it isn't sense.


Of course we can complain. First, std::format is already improved, so this is moot anyway. But more importantly, std::format always caught the error & threw a well defined exception. printf() does no such thing. It has no validation that your inputs matched the format spec. std::format always had that. So does std::cout & iostreams, for that matter.


Interesting article; C++ as “C and then some” was clearly an irresistible marketing term for many of the programmers from that time that I’m sure Bjarne indulged himself in saying once or twice too.


[1999]


Were people of that opinion back in the day?

I started* with Pascal, but I then moved to C++ the next semester. I only learned C as essentially a subset of C++. This was like 1996/97.

*Although I guess my first programming experience was with DOS/Windows Batch file scripting and some BASIC. Introduction to Programming with Pascal was the first programming language I took in as a class.


I was, yeah, if you mean using C++ as a better C but being slow to adapt the STL, standard library and associated concepts. We were doing a lot of device driver development and other constrained stuff back then (early 90s) so the "C++ as a higher-level language" vs. "C++ as incrementally improved C" was much discussed. Also, OO vs. generic programming was a hot topic, and still is.

It's always hard to extend a language culture to include libraries, as some people will prefer to adapt or roll their own. If it's not automatically part of the language the community will fracture.


Added. Thanks!


Doesn't this need a (1999) at the end? This is from before C++11.


Added. Thanks!


.




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

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

Search: