Tuesday, July 16, 2013

C++ and compile-time guaranteed pointer safety?

Personally, I would find this feature useful, but I am not a C++ expert by any means. ;-)

Do you think this would be a useful feature to add to the TR, and then later the standard after C++14? Let me know if you have any alternative suggestions within the currently available or already planned feature set to address this issue.

Oh, and if you do not know what this all means, here you can find the explanation of a good reference design: http://static.rust-lang.org/doc/tutorial.html#borrowed-pointers

8 comments:

  1. Sorry Laszlo, but how should this work? You'll never be able to do this! Pointers which come out of e.g. memory allocations or hardware interaction in general are inherently runtime features. You _cannot_ make them compiletime checked?!

    The rust link you show there is a feature C++ also has, namely references... Or what am I missing?

    ReplyDelete
    Replies
    1. 1) I am not sure I follow the comment, but in a nutshell: you could detect the scope during the compilation for the "frozen" pointer.

      Perhaps, you think, it is like an ultimate replacement? Nope, it is not. Back to rust, it still has owned and shared boxes. In addition, it also has managed box to be precise.

      2) Yes, there have been blog posts about C pointers or references as the closest features to this desired, but even then, they do not have any guarantee for the object being valid as long as the pointer is.

      Delete
    2. Compile-time safety for memory allocations works as follows in Rust:

      1. Every pointer has either a single owner (~T) or shared ownership (@T) - like unique_ptr and shared_ptr in C++.

      2. From one of these pointers, you can borrow a reference - creating a borrowed pointer (&T)

      So far, so much like modern C++. The important difference however is that the rust compiler ensures that any borrowed pointers do not outlive the owner(s).

      In C++, it is easy to store a reference or pointer to an object somewhere which later becomes invalid due to the target object having been destroyed. This is what rust prevents.

      Delete
    3. Yeah, Robert is right. Sorry about not having been so elaborate so far.

      So here comes a summary from me as well, although probably not much addition to Robert's comment. He probably also has deeper understanding, but anyway:

      The borrowed pointer is just a regular pointer at runtime as it is a static enforcement.

      You can easily have a reference that can become invalid or/and dangling. That is because it cannot be modified only in that particular code path. The example(s) would be the following for instance:

      a) Take a reference into a container and then resize it, and resize it.

      c) Make a reference that outlives where it is pointed at.

      d) Take the reference inside a tagged union and then change the type of the tagged union.

      Again, const only means it cannot be modified on that path through that particular reference instance. However, it can be modified through another "handler".

      So, in principle, rust has a region system inferring the lime time of objects.

      Rust does not make a difference between "it might be modified" or "it will be surely modified". It handles both the same way in this context which means it might be modified, so the borrowed pointer could become dangling, invalid, and so forth. Hence, rust has this "immutable" management which is different to const for that reason.

      The borrow checker does do control flow analysis among other things.

      It is surely a compromise so that you would get a compilation time safety even if the logic is that in a certain code path the pointer would not be modified. Put it this way: that is the best that can be done during the compilation time if you wanna get type safety without further performance overhead. :)

      Also, there is the point of mutable borrowed pointer which is slightly different to what you would think as "non-const" borrowed pointer. Basically, you can have multiple immutable borrowed pointers, but only one that is mutable. It must be the only one. Also, that does not take the ownership either.

      You could kinda achieve that with weak pointers, but they would bring their own dynamic overhead.

      Overall, it would be a good addition to the C++ standard to add this "immutable" concept, which is again, not const, but it would require a bit of work from the std implementations, I believe.



      Delete
  2. What about Smart pointers in Boost? They are template so they are checked at compiled time!

    http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/smart_ptr.htm

    ReplyDelete
    Replies
    1. How can they compete with borrowed pointers? As for me, they seem to be different.

      Delete
    2. To be thread safe you can use shared pointers
      http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/shared_ptr.htm

      Delete
    3. Shared pointers do not freeze the pointer, and what they achieve is happening during the runtime, not compilation.

      Delete