monkeyelite 1 day ago

Initialization does look insane. But as with most C++ complexity this is inherent.

Lists of the “good parts” of C++ over C usually include RAII. But f we imagine starting with C and adding C++ features to see when complexity explodes. I think the worst offender is constructor/destructor.

They require the language to perfectly track the lifetime of each member of every structure. If you resize a vector, every entry must call a constructor. If exceptions are possible, but insert little cleanup calls into all possible code paths.

Want to make a copy of something? Who is responsible for calling constructor/destructor. Want to make a struct? What if one member requires construction? How do you handle a union?

The result is micromanaging and turning most operations into O(n) init/cleanup calls.

The modern C approach avoids all of this and allows you to manage pieces of memory - rather than values. Zero initialize or leave uninitialized.

So what do we lose? Well classes own resources. If you have a vector<MyObject> and MyObject has a member vector<Items> then we should be able to cleanup without looking inside each member of each element.

I think we should separate resource allocation from use. Allocators are the things that care about cleanup, move, etc. This should be the exception - rather than the default way to think about structs.

2
gpderetta 1 day ago

> Want to make a copy of something? Who is responsible for calling constructor/destructor.

What do you mean? The compiler will do it for you.

> This should be the exception - rather than the default way to think about structs.

the way that RAII in C++ recursively construct and destroys arbitrary object graphs is extremely powerful. It is something that very few other languages have (Rust, any other?). It should definitely be the default.

> I think we should separate resource allocation from use. Allocators are the things that care about cleanup, move, etc.

I'm not sure what you mean by use. If you mean we should separate allocation from construction, I agree! But then so does C++. They are tied by default, but it is easy to separate them if you need it.

monkeyelite 1 day ago

> What do you mean? The compiler will do it for you.

It will. And the only cost to you is you have to understand initializer lists, pod types, copy constructors, move constructors, launder, trivially_destructible, default initialization, uninitialized_storage, etc.

> the way that RAII in C++ recursively construct and destroys arbitrary object graphs is extremely powerful

And extremely complex.

The big fallacy here is that you would want to manage resources at the individual node level - rather than for a batch.

> I'm not sure what you mean by use

It’s similar to the idea of arenas (also made difficult by constructors btw). You can make sophisticated systems for managing allocations of individual nodes in a graph - like reference counted smart pointers. Or you can avoid the problem entirely by deallocating the whole group at once.

Imagine if C++ structs were required to be pod and classes were not. Then you could always know that a struct can be trivially allocated/deallocated etc.

Then you could design data structures for pod types only that didn’t have to worry about O(n) cleanup and init.

bluGill 1 day ago

There is often value in putting a class in a struct. Your proposed rule of struct is POD means there will be many less structs, and force people to think about POD or not when the vast majority of the time that doesn't matter to them.

monkeyelite 1 day ago

> There is often value in putting a class in a struct.

And what’s the cost?

Note that a reference to a class is still POD. It just doesn’t have ownership.

Also I’m not making a specific policy proposal. Im identifying constructors and destructors as the source of complexity. What should we do about it?

> and force people to think about POD or not

No such thing as feature you don’t have to understand. The reason this article exists is that C++ programmers must deal with complex initialization behavior.

Can a C++ programmer not understand move semantics? Or copy constructors?

bluGill 1 day ago

Most of the time the cost is not worth worrying about.

Most of the time you don't need to think about move or copy - the compiler does the right thing for you. When there are exceptions it is generally when you need to disable them which is simple enough. When you think about constructors you only need to think about the local class in general, and not how they interact with the larger world.

constructors and destructors eliminate a lot more complexity than they solve. They enable RAII and thus clear ownership of everything (not just memory!).

monkeyelite 13 hours ago

Sounds like C++ has everything you want and is just the right amount of complexity for you.

bluGill 6 hours ago

Every version of c++ has added something I want and overall makes the language better. backward compatibility means I can start using the new stuff without rewritting the old and so I can take advantage of it now. (At least somewhat - calling old APIs is weird)

gpderetta 1 day ago

> Imagine if C++ structs were required to be pod and classes were not. Then you could always know that a struct can be trivially allocated/deallocated etc.

static_assert(is_trivial_v<T>)

monkeyelite 14 hours ago

Unfortunately other people aren’t writing simple C structs they are using the full feature set of C++ and I have to use their code.

So no this doesn’t solve C++ complexity including the initialization problem.

AlienRobot 1 day ago

I'm not very good with C++, so one time I tried to use RAII to do all sorts of neat GUI things, like freeze property events and ref count uses of files to know when to freed them from memory.

Essentially instead of doing

    object.freeze();
    object.setProperties(...);
    object.thaw();
I tried to do

    {
        Freezer freezer(object);
        object.setProperties(...);
    }
EVERY time I need a start/end matching function I used RAII instead because it looked neat. I tried to make "with" from Python in C++.

The problem is that exceptions thrown in a destructor can't be caught, which made this coding style practically impossible to use. Thawing the properties meant dispatching property change events, which meant that event handlers all around the app would be executed inside a destructor unaware that they were being executed inside a destructor. If any of these threw an exception, the whole thing crashed. In hindsight dispatching these events from a setter instead of from the main event loop also creates all sorts of trouble.

int_19h 16 hours ago

The closest equivalent to `with` from Python would be a higher-order function defined such that it can accept a lambda (i.e. it needs to be a template). Then you'd write something like:

   object.batchEvents([&]{
     object.setProperties(...);
   });
(for bonus points, pass object reference as argument to the lambda)