How do library developers decide when to throw an exception and exhibit undefined behavior?

Specifically, given the C ++ class constructor specifier string

, we have:

string (const string& str, size_t pos, size_t len = npos);
string (const char* s, size_t n);

      

which essentially have the same form / intent, except for the string and c-string. However, the specification of the exception is different:

If pos is greater than the length of the string, an out_of_range exception is thrown.

If n is greater than the array pointed to by s, this calls undefined.

I wonder why this is? As opposed to when is it recommended to throw an exception against the "undefined" permission? The answer would seem to depend on the following:

  • the client's ability to recognize an a priori error (i.e., the client is much less likely to recognize a potential file access problem than, for example, from the empty stack that the client is explicitly using)
  • customer interest in solving a potential problem (i.e., perhaps file access problems most often need to be handled because they are less predictable?)
  • what "makes the most sense" from a developer's point of view (perhaps the string version throws out because it's more related to C ++ style, hence the semantics of exceptions)
+3


source to share


3 answers


The difference in the two cases is simply that in the first case the implementation can check if there is an error in the passed parameter, and in the second it is impossible (there is no way to check which is the length of the string, since only the pointer is passed).

Thus, the general rule could be as follows:



  • If possible, check for a possible failure condition and throw an exception;
  • otherwise nothing can be done from the point of view of the executor and in the function document specification the possibility of undefined behavior.
+3


source


The library developer must implement the behavior specified by the C ++ standard, and the C ++ standard states:

basic_string(const basic_string& str,
             size_type pos, size_type n = npos,
             const Allocator& a = Allocator());

      

Required: pos <= str.size ()
Throws: out_of_range if pos> str.size ().

When

basic_string(const charT* s, size_type n,
             const Allocator& a = Allocator());

      



the standard says simply:

Requires: s points to an array of at least n charT elements.

(All from & sect; 21.4.2 [string.cons])

Since the standard does not specify what happens in the second case if the requirement is not met, that is, the behavior is undefined, leaving the library implementation free to do whatever it wants to do. However, there is no way for the library to actually detect the fact that this requirement has not been met, so it can be assumed that this requirement has been met and then allow the program to interrupt in some way the hardware responds to reading the invalid address.

If you are looking for a rationale, you must look for a rationale for the standard. The librarian has little or no flexibility.

+2


source


The reason for the behavior in your example is pretty obvious.

  • In the first case, the size of the copied string is known; this is one of the object's attributes. Just check this setting on this one.
  • In the second case, you only have a pointer. Generally, you cannot tell how large the corresponding memory area is. Therefore, you cannot check if another parameter is correct.
+1


source







All Articles