Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Not having RAII is precisely the reason I prefer C over C++ or Rust. I WANT to be able to separate allocation from initialization.

I'm currently working with Arduino code and the API is a mess. Everything has a second set of manual constructor/destructor, which bypasses type-safety entirely. All only to shoehorn having existing, but uninitialized objects into C++.



With Rust there are ways to do that on embedded (no heap). A wrapper StaticCell holds the allocation, then when you are ready you intialise it with the inner value. From then on work with a mut reference to the inner value. Being a bit verbose is the main downside AFAIK.

https://github.com/embassy-rs/static-cell


> I WANT to be able to separate allocation from initialization.

Which hardly ever makes sense, and is possible with clean C++ anyway...


> is possible with clean C++ anyway...

That's news to me; how?


Placement new?


I might also use placement new after taking a pointer to a stack object or global, but it won't prevent the original constructor from being run.


>Arduino code

Usually Arduino code is written by hobbyist that give zero care about "clean and abstraction".


Yes and that is another large part of work for me, but this pattern is mandated by the Arduino API themself and I don't see another way given the specification and the design of C++, short of switching over to C.


Both C++ and Rust allow that? Having niche behaviour not be the default makes sense, but both know it's needed and therefore allow it?

(C++ lets you malloc and then placement new (just casting the pointer like C does is UB, but it's being fixed for trivial types) and Rust has both plain alloc and Box<MaybeUninit<T>>)

There are a lot of other reasons not to use them, but yours is a made up strawman.


This isn't what people are talking about, you aren't understanding the problem

With RAII you need to leave everything in an initialized state unless you are being very very careful - which is why MaybeUninit is always surrounded by unsafe

    {
        Foo f;
    }

f must be initialized here, it cannot be left uninitialized

    std::vector<T> my_vector(10000);
EVERY element in my_vector must be initialized here, they cannot be left uninitialized, there is no workaround

Even if I just want a std::vector<uint8_t> to use as a buffer, I can't - I need to manually malloc with `(uint8_t)malloc(sizeof(uint8_t)*10000)` and fill that

So what if the API I'm providing needs a std::vector? well, I guess i'm eating the cost of initializing 10000 objects, pull them into cache + thrash them out just to do it all again when I memcpy into it

This is just one example of many

another one:

with raii you need copy construction, operator=, move construction, move operator=. If you have a generic T, then using `=` on T might allocate a huge amount of memory, free a huge amount of memory, or none of the above. in c++ it could execute arbitrary code

If you haven't actually used a language without RAII for an extended period of time then you just shouldn't bother commenting. RAII very clearly has its downsides, you should be able to at least reason about the tradeoffs without assuming your terrible strawman argument represents the other side of the coin accurately


> malloc

Yes, that's heap allocation. I'm talking about automatic allocation, by the compiler not getting a pointer from a library function. Like that:

    Connection connections[200];
This will call the constructor, which forces me to write the class in a way that has `bool initialized`, and provide a random other method with poses as a second constructor. And now every function has to do a check, whether the constructor was called on the object or I just declare it to be UB and completely loose type safety.




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

Search: