Hacker News new | past | comments | ask | show | jobs | submit login

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™.

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.




> And I won't get started with C strings, which are just a historical BadIdea™.

What's the better alternative to null-prefixed strings you are referring to, length prefixed?


Null terminated strings are the root cause of millions, if not billions of security vulnerabilities caused by buffer overflows. All modern languages have shunned null-terminated strings in favour of something in the likes of

  struct string_type {
    char *ptr; // or char[]
    size_t size;
  }
because the whole idea of scrolling a whole string just to get its length it's an immense waste of CPU time. It has been optimized to death given that C does that for historical reasons¹, but still it's unwieldy and less safe.

For instance, C strings make 0-cost string slicing impossibile: for instance, if you wanted to slice a string to get str[2;4] (i.e. a 2 char string from position 2), it would be simply

  string slice { .data = str.data + 2, .size = 2 }
with the C++/Java/Rust/whatever approach. With C strings, given that you have to put a '\0' terminator at string end, you're forced to allocate memory and memcpy() the string, or to corrupt the original string.

¹: size_t is much bigger than char (2x on 16 bit, 8x on 64 bit machines), so it made sense on a memory constrained machine like the PDP-11 to waste CPU time in order to save memory.


Those would be Pascal Strings (length at the start, then the bytes). Those make for very easy to write code, but they come with a few issues, but in general they are simpler and give better behavior than null-terminated strings. However, as for a bit of a historical bagagge, it seems we got used to null-terminated strings and just went on with them.


Only in C, even C++ has since the early days adopted proper strings on its standard library, initially the compiler specific ones from the early 90's, then the standard one.


Sure, its just that these kind of strings were historically called Pascal-strings [0] because they were the way Pascal implemented strings.

[0] And still are! For example, Python refers to them as such, see the "p" (lowercase p) character code here: https://docs.python.org/3/library/struct.html#format-strings


Not all C++ string libraries use Pascal strings.

Some of them use a fat pointer instead, with a mix of counter, pointer to the end, and null terminator for C compatibility.


That's ingenious, but I don't think I've heard a name for those kind of strings.


One thing is certain, they aren't Pascal strings.


How are the compile times? That's a really big point in the article and you don't mention them at all.


Hmm, they are tolerable to say the least. I think lots of people complaining with bad compile times are probably developing on underpowered laptops, which are definitely not the right tool for C++ development. I've never had an issue of long compile times on a reasonably specced desktop computer. Setting up ccache sure helps at times.

The wall of text generated by GCC/Clang/MSVC when template substitutions fail is a much worse issue, IMHO. Thankfully concepts help a lot in cutting off SFINAE earlier, so you don't get flooded with useless errors about the compiler trying to substitute random constructors in places or something.


Would you mind sharing a link to the ISO C++ guidelines you mention?



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.


Is the library open source? If so, would you share a link?


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.




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

Search: