← Back to context

Comment by Panzerschrek

1 day ago

> the mess in C++ is way worse

What is mess in C++? Yes, it has some shady parts and is more complex than C, but this complexity provides expressiveness and type safety. And you can always use only features from C++ you find useful.

> also contribute to projects that introduce C++ into a C code base, and I really wish they hadn't done this.

What is problematic in these projects other than unfamiliarity of C++ for developers previously used only C?

> What is mess in C++?

Just a couple examples:

How do you make a shallow copy of an object in C++, how do you do the same thing in C? Knowing I can just memcpy _any_ struct in C and have a valid shallow copy is pretty huge.

How do you create a stable ABI in C++? How do you make one in C? In C, knowing that all my exported functions will mostly just work and be stable as long as I don’t change the interface is also huge and allows you to create cross language bindings pretty trivially.

  • You can’t necessarily do that in C either, it depends on if your object is in a container that relies on pointer stability. Intrusive linked lists are common in C and they get broken by this, for example.

  • > How do you make a shallow copy of an object in C++

    Shallow copies aren't generally possible for classes storing something indirectly, since it violates ownership semantics. That's not how things are done in C++.. Operator = is usually used for making copies, which is optimized to memcpy for POD structures, but for something more complex may perform extra work for doing an actual deep copy.

    > How do you create a stable ABI in C++

    It's a complex topic. Basic ABI for calling functions is identical to C. But one need to keep in mind, that type layouts and internal implementations of library types (like containers) may differ from implementation to implementation and from version to version.

    > knowing that all my exported functions will mostly just work and be stable as long as I don’t change the interface

    Nothing prevents you doing this in C++. You can have C-style external interface and use all goods of C++ internally.

    • Yeah, and that's the reason why people prefer to write in C.

      "That's not how things are done in C++" I like C, because I can just decide for myself how things are done here.

    • > Nothing prevents you doing this in C++.

      You left out the part of my comment where I said “pretty trivially” lol. C++ is mostly a superset of C, which is why I’m not arguing that it’s impossible to do these things in C++. I like C++ too, and think some of the things in there are great. But the simplicity of C is nice. I would often get overwhelmed thinking about all the different constructors in C++ and what I needed to do to ensure that I didn’t accidentally tank performance by copying objects around everywhere (and potentially calling some invisible constructor that did extra work in the copy function). In C, I know when I memcpy it’s just gonna move some bytes around.

      Overall, I really don’t care all that much one way or the other. But I can see how people could prefer C to C++. I could also see how people prefer C++. I really really miss function overloading and <string.h> when I use C

    • You seem to be asking question "why people use hacks in C instead of coding in other languages which are more C++ like". Then you describe what is to be C++ like. Well, maybe the issue is that certain people precisely want to avoid the features you just described. Have you considered that?

  • Calling memcpy on any random old struct in C is how you get bugs. If the struct contains other pointers: who is in charge of freeing them? What if the struct has internal pointers (a pointer pointing to a subobject of the struct)? You are playing with fire and you know it.

> And you can always use only features from C++ you find useful.

Ah, the old "programmers just need to be more disciplined when using C++".

Yeah, C programmers have never seen that argument before...

  • As if C programmers don’t have to be disciplined. Look if you are a C programmer you need to pick good parts of the language to write in as well. A beginner picked gets()? Oh the foes of the young and inexperienced. You picked VLA? Linus would breathe fire unto you.

    A difference between C++ programmers and C programmers is that C++ programmers have the awareness to know their language has bad parts to avoid, but C programmers are oblivious to the bad parts until someone uses it, but otherwise sing paeans for the language.

My macro-vector types are type safe. In fact this is the point.

I do not think C++ is more expressive or type safe.

  • In C, assigning the return value from malloc() to a specific type merely produces a warning:

      $ echo -ne "#include <stdlib.h>\nint foo() { return malloc(42); }" | gcc -xc -fsyntax-only -c -
      <stdin>: In function ‘foo’:
      <stdin>:2:20: warning: returning ‘void *’ from a function with return type ‘int’ makes integer from pointer without a cast [-Wint-conversion]
    

    Whereas in C++ it's a hard error:

      $ echo -ne "#include <stdlib.h>\nint foo() { return malloc(42); }" | gcc -xc++ -fsyntax-only -c -
      <stdin>: In function ‘int foo()’:
      <stdin>:2:26: error: invalid conversion from ‘void*’ to ‘int’ [-fpermissive]
    

    Does this not count as "more type safe" to you?

    • In both languages this is a constraint violation, and it is fully at the discretion of the compiler on whether it emits a warning or error (often controllable by compiler flags). For my C compiler (GCC) this is an error.

    • What version is your compiler? As of GCC 14 and clang 15 an implicit pointer conversion to int is an error, not merely a warning; and this is without specifying any special conformance or warning modes. I think implicit int conversions have been a constraint violation since at least C99, requiring a diagnostic, though I'm not sure if the switch to an error instead of a warning was prompted by a change in wording in C23 or just a general change in attitude and less concern with failing on pre-existing (but wrong) code.

      2 replies →

    • No, this counts as C++ is stupid to me. The memory returned by malloc is UNTYPED. The sole reason why void as a type exists is to convey the notion of no type, so that it needs to be assigned to a type by a programmer. If you mean "byte region, please cast before use" that's 'char *'. Since C++ now forces me to write a cast, it effectively forces me to hide and silence errors.

      I do want an error when I violate types, I do not want it for void, because that's the whole meaning of void. C++ manages to make it the worst of both worlds.

      2 replies →