Pre- and post-condition checking in C++

This is a discussion on Pre- and post-condition checking in C++ within the C++ Programming forums, part of the General Programming Boards category; Food for thought. Code: #include <cstring> #include <boost/utility/result_of.hpp> struct condition_violation_exception { condition_violation_exception(const char *file_p, int line_p) : line(line_p) { std::strncpy(file, ...

  1. #1
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,237

    Pre- and post-condition checking in C++

    Food for thought.

    Code:
    #include <cstring>
    #include <boost/utility/result_of.hpp>
    
    struct condition_violation_exception
    {
       condition_violation_exception(const char *file_p, int line_p)
          : line(line_p)
       {
          std::strncpy(file, file_p, sizeof(file) - 1);
          file[255] = '\0';
       }
    
       char file[256];
       int line;
    };
    
    struct precondition_violation : public condition_violation_exception
    {
       precondition_violation(const char *file_p, int line_p)
          : condition_violation_exception(file_p, line_p)
       {
       }
    };
    
    struct postcondition_violation : public condition_violation_exception
    {
       postcondition_violation(const char *file_p, int line_p)
          : condition_violation_exception(file_p, line_p)
       {
       }
    };
    
    template <typename F, typename Pre, typename Post, typename Result>
    struct eval_s
    {
       static Result eval(F func_p, Pre pre_p, Post post_p)
       {
          if(!pre_p()) throw precondition_violation(__FILE__, __LINE__);
          Result r = func_p();
          if(!post_p()) throw postcondition_violation(__FILE__, __LINE__);
          return r;
       }
    };
    
    template <typename F, typename Pre, typename Post>
    struct eval_s<F, Pre, Post, void>
    {
       static void eval(F func_p, Pre pre_p, Post post_p)
       {
          if(!pre_p()) throw precondition_violation(__FILE__, __LINE__);
          func_p();
          if(!post_p()) throw postcondition_violation(__FILE__, __LINE__);
       }
    };
    
    #ifndef NDEBUG
    template <typename F, typename Pre, typename Post>
    typename boost::result_of<F>::type eval(F func_p, Pre pre_p, Post post_p)
    {
       return eval_s<F, Pre, Post, typename boost::result_of<F>::type>::
          eval(func_p, pre_p, post_p);
    }
    
    #else
    
    template <typename F, typename Pre, typename Post>
    typename boost::result_of<F>::type eval(F func_p, Pre pre_p, Post post_p)
    {
       return func_p();
    }
    #endif
    
    inline bool no_conditions()
    {
       return true;
    }
    Questions to ask yourself:

    1. Why have two classes: precondition_violation and postcondition_violation, which are identical?

    2. Why is there a need for the eval_s indirection? What am I trying to do that I cannot do with a template function?

    3. What happens to the condition checks if NDEBUG is defined?

    4. After answering #2, how come the same problem doesn't crop up in eval<F, Pre, Post>() when NDEBUG is defined?

    5. Are there any bugs?

    6. Is there any point?
    Last edited by brewbuck; 03-20-2008 at 12:24 AM.

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,188
    1: Why not have two exception classes which happen to be mechanically identical? For that matter, why not have $(Count) exception classes which happen to be mechanically identical? (The keyword here being 'exception'. Without a class/type for each scenario recovery code would have to check a flag/string/value in every catch block. With many exception types to choose recovery code can be very effective/efficient by only attempting to handle specific exceptions. Further, without the complexity of checking a flag/string/value, recovery code need never re-throw because the dynamic subtype isn't handled--assuming a reasonable is-a relationship. This will help prevent slicing of exception classes. That said, having $(Count) implementations isn't good programming. It is far better for the actual implementation type to be a template accepting a "type tag" as a parameter.)

    2a: There isn't a real need. Some indirection will always be needed because the return value of the target function needs to be forwarded to the calling function and the postcondition check must be made after the execution of the target function. That isn't to say this particular indirection is necessary. Other implementations might use RAII to perform the precondition/postcondition check in which case a more normal return statement is possible--therefore it would need no special handling of void.

    2b: Nothing. The indirection here is only intended to forward the target return value back to the calling function. The specialization handling 'void' is a bit of a "red herring". It only needs this specialization because of the way the forwarding had been implemented. This implementation isn't the only "correct" way to handle forwarding and still check a precondition/postcondition.

    3: Nothing. The checks simply aren't performed. This is the correct behavior. Out of "debug mode" the code of the implementation of this interface has presumably been proven correct for all valid input. (That this is usually impossible is irrelevant. It only needs to perform to some standard. In a critical situation you would likely want the interface to produce invalid results rather than allow a useless exception to be propagated. That is, if the implementation can't produce the correct results for valid input it is unlikely that a client can correct whatever problem so that a second attempt can be made.) Out of "debug mode" the client, code using this interface, has presumably provided a check, to disallow all invalid input, in the most appropriate place. (It is simply too expensive for every interface to performed validation for all input every execution. It is better for the client to perform validation because the client has a better idea of when to perform it, and it is rarely useful for execution to halt when it can be skipped. Of course, it is always nice for an interface to provide a validation routine, but that isn't so usual.)

    4: Because the postcondition check is no longer performed the return value of the target function can be forwarded directly to the calling function. (The code 'Result r = func_p();' is not then needed with this implementation.)

    5: It depends on your perspective and the definition of "bug", but at the very least some compilers will not like 'return f()' in a function declared as "returning void" even if 'f()' really does "return void". A major logic bug, in my opinion, is the exception mechanism proposed here. Yes, using multiple types for multiple exceptions is good programming. I'm talking about what information is provided by the exceptions. Yes, it is true that knowing the file and the line number of an error is useful, but this isn't the information this implementation provides. Assuming a fairly standard header, named 'my_prepost.h', the only information this implementation could ever provide is useless. (It will only ever provide: (file='my_prepost.h', line=40), (file='my_prepost.h', line=42), (file='my_prepost.h', line=52), (file='my_prepost.h', line=54).) If you are going to use something like this it is crucial that the '__FILE__' and '__LINE__' macros be passed to the 'eval' function by way of a macro very like 'assert'. (It is necessary that 'eval' be augmented to take '__FILE__' and '__LINE__' as the first arguments. In this way such an assert macro could forward this information but otherwise mimic what you have here.)

    6. It depends on whether or not any actual source level precondition/postcondition check would be useful. I find them mostly useless.

    Soma

  3. #3
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,237
    Who is this masked man/woman?

    Extremely good comments. This was something I threw together last night inspired by some other posting activity here.

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    4,188
    I'm Zero Soma Valintine, the Phantom of the Astral Planes.

    I'm also male, and having to answer this question bothers me. I've heard of them, but I've never actually met a girl Soma. Do they really exist?! O_o

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Unknown memory leak with linked lists...
    By RaDeuX in forum C Programming
    Replies: 6
    Last Post: 12-07-2008, 03:09 AM
  2. New compiler - Weird errors -,-.
    By Blackroot in forum C++ Programming
    Replies: 8
    Last Post: 08-27-2006, 07:23 AM
  3. Conflicting types of typedef error
    By advocation in forum C++ Programming
    Replies: 4
    Last Post: 03-22-2005, 05:26 PM
  4. Post increment and pre increment help
    By noob2c in forum C++ Programming
    Replies: 5
    Last Post: 08-05-2003, 03:03 AM
  5. waiting for help re; pre post
    By OPENCCT in forum C Programming
    Replies: 1
    Last Post: 12-02-2001, 05:20 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21