Search code examples
c++undefined-behavior

Is there a safe version of C++ without undefined behaviour?


Undefined behaviour in C++ can be really hard to debug. Is there a version of C++ and standard library which does not contain any undefined behaviour but rather throws exceptions? I understand that this will be a performance killer, but I only intend to use this version when I am programming, debugging and compiling in debug mode and don't really care about performance. Ideally this version would be portable and you would be able to easily switch on/off the undefined behaviour checks.

For example, you could implement a safe pointer class like so (only check for null pointer, not actually if it points to a valid block of memory):

template <typename T>
class MySafePointer {
     T* value;
public:
      auto operator-> () {
          #ifndef DEBUG_MODE
          assert(value && "Trying to dereference a null pointer");          
          #endif
          return value;
      }
      /* Other Stuff*/

};

Here the user only needs to #undef DEBUG_MODE if you want to get your performance back.

Is there a library / safe version of C++ which does this?

EDIT: Changed the code above so that it actually makes more sense and doesn't throw an exception but asserts value is non-null. The question is simply a matter of having a descriptive error message vs a crash...


Solution

  • Undefined behavior

    Undefined behavior means that your program has ended up in a state the behavior of which is not defined by the standard.¹

    So what you're really asking is if there's a language the standard of which defines every possible scenario.

    And I can't think of one language like this, for the simple reason that programs are run by machines, but programming languages and standards and written by humans.

    Is it always unintentional?

    Per the reason explained above, the standard can have unintentional "holes", i.e. undefined behavior that was not intentionally allowed, and maybe not even noticed during standardization.

    However, as all the "is undefined behavior" sentences in the standard prove, many times UB is intentionally allowed.

    But why? Because that means giving less guarantees to the programmer, with the benefit of being able to make more optimizations or, equivalently, to not waste time verifying that the user is sticking to a defined contract.

    So, even if the standard had no holes, there would still be a lot of cases where UB is stated to happen by the standard, because compilers can take advantage of it to make all sort of optmizations.²

    The impact of preventing it in some trivial case

    One trivial case of undefined behavior is when you access an out-of-bound element of a std::vector via operator[]. Exactly like for C-style arrays, v[i] basically gives you back *(v_ + i), where v_ is the pointer wrapped into v. This is fast and not safe.³

    What if you want to access the ith element safely? You would have to change the implementation of std::vector<>::operator[].

    So what would the impact be of supporting the DEBUG_MODE flag? Essentially you would have to write two implementations separated by a #ifdef/(#else/)#endif. Obviously the two implementation can have a lot in common, so you could #-branch several times in the code. But... yeah, my bottom line is the your request can be fulfilled by changing the standard in such a way that it forces the implementers to support a two different implementations (safe and fast/unsafe and slow) for everything.

    By the way, for this specific case, the standar does define another function, at, which is required to handle the out-of-bound case. But that's the point: it's another function.


    (¹) In which case the standard (my emphasis)

    places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation)

    (²) I really recommend reading this answer by Nicol Bolas about UB being absent in constexprs.

    (³) This and other examples of UB are listed in this excellent article; search for Out of Bounds for the example I made.