2

In procedural languages where functions are a key player, the design by contract paradigm basically says that there is an agreement between a function that takes parameters and the caller.

The agreement goes something like "if the caller ensures that the preconditions of the function are met, then the function will behave in an expected manner and/or return expected values."

If we write code strictly in this way, then the caller alone is responsible for ensuring proper inputs to functions. But in the name of defensive programming, it seems wise to include internal safeguards in case the caller does something stupid.

When it come to software architecture and design, what is the best approach here?

Mode77
  • 991
  • 8
  • 28
  • It depends on how fast you want the code to run. C by its nature will let you shoot yourself in the foot, and some of the protection comes from the compiler. – Weather Vane Oct 13 '18 at 19:13
  • @Ron: An example of a [precondition](https://en.wikipedia.org/wiki/Precondition) is in C 2018 7.4 1, about ``: “In all cases the argument is an **int**, the value of which shall be representable as an **unsigned char** or shall equal the value of the macro **EOF**. If the argument has any other value, the behavior is undefined.” The signature alone does not specify the requirements for calling the function; there are additional criteria that must be met. – Eric Postpischil Oct 13 '18 at 19:17
  • If the called routine tests a purported precondition and handles it appropriately, the condition is then not actually a precondition but is merely a valid subset for calling the function. So really, the question here is, how should one design code? That is a matter of goals (such as performance versus safety), and answers may be greatly influenced by opinions. I would almost vote to close this as primarily opinion based, but I think it is significantly but not primarily opinion based. There are fact-based reasons for checking versus not checking. – Eric Postpischil Oct 13 '18 at 19:22
  • For performance reasons, the caller should check. For security reasons, the function may have to check. In debug builds the function can fail an assertion if the conditions are not met. – 1201ProgramAlarm Oct 13 '18 at 21:04
  • If things go well, C++20 will add [Contracts](https://en.cppreference.com/w/cpp/language/attributes/contract). That will help make these things more obvious _in code_ rather than in comments or through tribal lore. – Eljay Oct 13 '18 at 22:43

2 Answers2

3

It's the responsibility of the caller. For example, what could an implementation of strlen do if passed a null pointer? The only thing it could do is abort the program - which is a viable, if drastic, option in C. In C++ it could throw an exception (but not if it adheres to the C++ Standard), but dealing with that exception would be very difficult. So the only sensible solution, that allows the program to keep running in a known state, is for strlen not to be called with a null pointer as a parameter, placing the onus for checking this on the calling code.

  • @X.Code `strlen` could possibly return `-1` in this case, were it not that `size_t` is unsigned. But C is implemented in the way that the language designers wanted, modified by subsequent standards. If you want a safe language then C is not for you. – Weather Vane Oct 13 '18 at 19:17
  • 1
    @Weather If it did, every single piece of code that called `strlen` would have to test for it. It's far better to say "calling strlen with a null pointer is undefined". This tells the programmer they should not call it in this manner. It's like divide by zero - would you really want a special return code every time you did a divide operation? –  Oct 13 '18 at 19:19
  • ...which could have been intercepted sooner by checking the for `NULL` *before* calling `strlen` instead of my hypothetical `-1` *after* the call. – Weather Vane Oct 13 '18 at 19:22
  • @Weather "which could have been intercepted sooner by checking the for NULL before calling strlen " - that is exactly what my answer says. –  Oct 13 '18 at 19:24
  • I wasn't arguing with it. My comment was directed at OP, who asks if the function passes the error back to the caller and your answer did not cover that. A segfault makes recovery difficult. – Weather Vane Oct 13 '18 at 19:24
1

As Neil was saying, the checking is the caller's responsibility. You said,

it seems wise to include internal safeguards in case the caller does something stupid.

To describe why this is not the ideal aproach, I will expand on Neil's example, which used checking for null pointers being passed to strlen, imagine two scenarios:

  1. Function that called strlen checks the input beforehand.
  2. strlen checks its arguments internally before operating on them.

There is no difference in efficiency between these two methods if you are making a single call to strlen. However, imagine that instead of making a single call to strlen, you are calling strlen, strstr, and other string functions multiple times in succession. In scenario 1, whether you 1, 2, 10, or 100 calls to functions from the string library, you only have to check for bad inputs once. In scenario 2, each call is forced to check the input making the process much slower.

jahneff
  • 73
  • 1
  • 5