//****************************************************************************** // RCF - Remote Call Framework // Copyright (c) 2005 - 2007. All rights reserved. // Consult your license for conditions of use. // Developed by Jarl Lindrud. // Contact: jlindrud@hotmail.com . //****************************************************************************** #ifndef INCLUDE_UTIL_PREPOSTCONDITION_HPP #define INCLUDE_UTIL_PREPOSTCONDITION_HPP // BCB problems as usual #ifdef __BORLANDC__ #define DISABLE_PREPOSTCONDITIONS #endif #ifdef DISABLE_PREPOSTCONDITIONS #undef DISABLE_PREPOSTCONDITIONS //******************************************************* // Noop prepostcondition implementation // Provide no-op macros so that client code compiles, even if it doesn't do anything. namespace util { namespace PrePostCondition { struct Dummy { template const Dummy &operator()(const T &) const { return *this; } }; } // namespace PrePostCondition } // namespace util #define UTIL_PRECONDITION(print, fail, condition) if (false) util::PrePostCondition::Dummy() #define UTIL_POSTCONDITION(print, fail, condition) if (false) util::PrePostCondition::Dummy() #define UTIL_BEGIN_INVARIANT(type) void util_PrePostCondition_dummy() { #define UTIL_DEFINE_INVARIANT(condition) if (false) util::PrePostCondition::Dummy() #define UTIL_END_INVARIANT() ;} #define UTIL_POST(arg) arg #define UTIL_PRE(arg) arg #else //******************************************************* // Proper prepostcondition implementation #include #include #include #include #include #include #include #include #include #include "Platform/Platform.hpp" #include "VariableArgMacro.hpp" // Define the class invariant namespace util { namespace PrePostCondition { // Define variable arg macro for use in defining class invariant struct InvariantArgsFunctor : public Functor<> { InvariantArgsFunctor() : invariantArgs(dummy) { assert(0); } InvariantArgsFunctor(const std::vector > &invariantArgs, const std::string &condition) : invariantArgs(const_cast > &>(invariantArgs)), condition(condition) {} void deinit() { invariantArgs.push_back( std::make_pair( condition, this->args.str() ) ); } private: std::vector > dummy; std::vector > &invariantArgs; std::string condition; }; DECLARE_VARIABLE_ARG_MACRO( UTIL_INVARIANTARGSFUNCTOR, InvariantArgsFunctor ); #define UTIL_INVARIANTARGSFUNCTOR(invariantArgs, condition) util::PrePostCondition::VariableArgMacro(invariantArgs, #condition).UTIL_INVARIANTARGSFUNCTOR_A #define UTIL_INVARIANTARGSFUNCTOR_A(x) UTIL_INVARIANTARGSFUNCTOR_OP(x, B) #define UTIL_INVARIANTARGSFUNCTOR_B(x) UTIL_INVARIANTARGSFUNCTOR_OP(x, A) #define UTIL_INVARIANTARGSFUNCTOR_OP(x, next) UTIL_INVARIANTARGSFUNCTOR_A.notify_((x), #x).UTIL_INVARIANTARGSFUNCTOR_ ## next namespace Invariant { static bool dummy1; static std::vector > dummy2; enum Action { Evaluate, GetArgs }; } } } #define UTIL_BEGIN_INVARIANT(type) \ boost::function getInvariantFunctor() { return boost::lambda::bind( &type::getInvariant, this ); } \ boost::function > &)> getInvariantArgsFunctor() { return boost::lambda::bind( &type::getInvariantArgs, this, boost::lambda::_1 ); } \ bool getInvariant() { bool ret; invariant(util::PrePostCondition::Invariant::Evaluate, ret,util::PrePostCondition::Invariant::dummy2); return ret; } \ void getInvariantArgs(std::vector > &invariantArgs) { invariant(util::PrePostCondition::Invariant::GetArgs, util::PrePostCondition::Invariant::dummy1, invariantArgs); } \ void invariant(util::PrePostCondition::Invariant::Action action, bool &ret, std::vector > &invariantArgs) \ { \ ret = true #define UTIL_DEFINE_INVARIANT(invariant) \ ; \ if (action == util::PrePostCondition::Invariant::Evaluate) \ ret = ret && (invariant) ; \ if (action == util::PrePostCondition::Invariant::GetArgs) \ if (!(invariant)) \ UTIL_INVARIANTARGSFUNCTOR(invariantArgs, invariant) #define UTIL_END_INVARIANT() \ ; \ } // Defer evaluation of an argument until the containing condition is about to be verified #define UTIL_POST(arg) util::PrePostCondition::Post(*this, arg) #define UTIL_PRE // Define variable arg macros for pre- and post-conditions namespace util{ namespace PrePostCondition { inline std::string &replace(std::string &s, const std::string &strOld, const std::string &strNew) { std::string::size_type pos = 0; while ((pos = s.find(strOld)) != std::string::npos) s.replace(pos, pos + strOld.length(), strNew); return s; } inline std::string &replaceBoostLambdaVarWithPost(std::string &s) { return replace(s, "util::PrePostCondition::Post(*this, ", "POST("); } template inline void writeArg(std::ostream &os, const T &t, const char *name) { std::string strName = name; os << replaceBoostLambdaVarWithPost( strName ) << "=" << t() << ", "; } template inline boost::lambda::lambda_functor< boost::lambda::identity< const T & > > Post(X &, const T &t) { return boost::lambda::var(t); } template inline boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<1, boost::lambda::function_action<1> >, typename boost::lambda::detail::bind_tuple_mapper::type > > Post( X &, T(*fun)() ) { return boost::lambda::bind(fun); } template inline boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<2, boost::lambda::function_action<2> >, typename boost::lambda::detail::bind_tuple_mapper::type > > Post( X &obj, T(X::*fun)() ) { return boost::lambda::bind(fun, &obj); } template inline boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<1, boost::lambda::function_action<1> >, typename boost::lambda::detail::bind_tuple_mapper >::type > > Post( X &, T(__stdcall *fun)() ) { boost::function f(fun); return boost::lambda::bind(f); } class Print_ODS { public: void operator()(const std::string &arg) { Platform::OS::OutputDebugString( arg.c_str() ); } }; class Fail_Null { public: void operator()() {} }; class Fail_Assert { public: void operator()() { assert(0); } }; class DbCFailure : public std::runtime_error { public: DbCFailure(const std::string &msg) : std::runtime_error(msg) {} }; class Fail_Throw { void operator()() { throw DbCFailure("DbC failure"); } }; template class PrePostConditionFunctor { public: template T &cast(T *) { return dynamic_cast(*this); } void deinit() { if (condition() == false) { std::string msg = getType() == PreCondition ? "FAILURE: Precondition: " : "FAILURE: Postcondition: "; std::string strCondition = context.condition; replaceBoostLambdaVarWithPost(strCondition); formatHeader( msg + strCondition + " in " + context.function ); formatArgs(); print(); fail(); } if (invariant() == false) { std::string msg = getType() == PreCondition ? "FAILURE: Class invariant (on entry): " : "FAILURE: Class invariant (on exit): "; std::vector > invariantArgs; this->invariantArgs( invariantArgs ); for (unsigned int i=0; i void notify(const T &t, const char *name) { notify( (boost::lambda::constant(0), t), name ); } template void notify(const boost::lambda::lambda_functor &t, const char *name) { typedef typename boost::lambda::lambda_functor::nullary_return_type T; boost::function f = t; boost::function< void(std::ostream &) > g = boost::lambda::bind( writeArg< boost::function >, boost::lambda::_1, f, name ); this->argFunctors.push_back(g); } PrePostConditionFunctor &setConditionFunctor(boost::function condition) { this->condition = condition; return *this; } PrePostConditionFunctor &setInvariantFunctor(boost::function invariant) { this->invariant = invariant; return *this; } PrePostConditionFunctor &setInvariantArgsFunctor(boost::function > &)> invariantArgs) { this->invariantArgs = invariantArgs; return *this; } PrePostConditionFunctor &setContext(const char *condition, const char *file, unsigned int line, const char *function) { Context context = { condition, file, line, function }; this->context = context; return *this; } enum Type { PreCondition, PostCondition }; virtual Type getType() = 0; private: std::string header; std::string args; boost::function condition; boost::function invariant; boost::function > &)> invariantArgs; std::vector< boost::function > argFunctors; struct Context { const char *condition; const char *file; unsigned int line; const char *function; }; Context context; void formatHeader(const std::string &label) { struct timeb tp; ftime(&tp); int timestamp = static_cast( (tp.time % 60) * 1000 + tp.millitm ); int threadid = Platform::OS::GetCurrentThreadId(); std::ostringstream ostr; ostr << context.file << "(" << context.line << "): " << label << ": Thread-id=" << threadid << " : Timestamp(ms)=" << timestamp << ": "; header = ostr.str(); } void formatArgs() { std::ostringstream ostr; for (unsigned int i=0; iargs = args; } void print() { Print()(header + args + "\n"); } void fail() { if (!std::uncaught_exception()) { Fail()(); } } }; template class PreConditionFunctor : public PrePostConditionFunctor { Type getType() { return PreCondition; } }; template class PostConditionFunctor : public PrePostConditionFunctor { Type getType() { return PostCondition; } }; DECLARE_VARIABLE_ARG_MACRO_T2( UTIL_PRECONDITION, PreConditionFunctor ); #define UTIL_PRECONDITION(print, fail, cond) \ util::PrePostCondition::VariableArgMacro >() \ .setConditionFunctor(boost::lambda::constant(true) && cond) \ .setInvariantFunctor(this->getInvariantFunctor()) \ .setInvariantArgsFunctor(this->getInvariantArgsFunctor()) \ .setContext(#cond, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION) \ .cast( (util::PrePostCondition::VariableArgMacro > *) NULL ) \ .UTIL_PRECONDITION_A #define UTIL_PRECONDITION_A(x) UTIL_PRECONDITION_OP(x, B) #define UTIL_PRECONDITION_B(x) UTIL_PRECONDITION_OP(x, A) #define UTIL_PRECONDITION_OP(x, next) UTIL_PRECONDITION_A.notify_((x), #x).UTIL_PRECONDITION_ ## next DECLARE_VARIABLE_ARG_MACRO_T2( UTIL_POSTCONDITION, PostConditionFunctor ); #define UTIL_POSTCONDITION(print, fail, cond) UTIL_POSTCONDITION_(print, fail, cond, __LINE__) #define UTIL_POSTCONDITION_(print, fail, cond, lineNo) UTIL_POSTCONDITION__(print, fail, cond, lineNo) #define UTIL_POSTCONDITION__(print, fail, cond, lineNo) UTIL_POSTCONDITION___(print, fail, cond, instance##lineNo) #define UTIL_POSTCONDITION___(print, fail, cond, instance) \ util::PrePostCondition::VariableArgMacro > instance; instance \ .setConditionFunctor(boost::lambda::constant(true) && cond) \ .setInvariantFunctor(this->getInvariantFunctor()) \ .setInvariantArgsFunctor(this->getInvariantArgsFunctor()) \ .setContext(#cond, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION) \ .cast( (util::PrePostCondition::VariableArgMacro > *) NULL ) \ .UTIL_POSTCONDITION_A #define UTIL_POSTCONDITION_A(x) UTIL_POSTCONDITION_OP(x, B) #define UTIL_POSTCONDITION_B(x) UTIL_POSTCONDITION_OP(x, A) #define UTIL_POSTCONDITION_OP(x, next) UTIL_POSTCONDITION_A.notify_((x), #x).UTIL_POSTCONDITION_ ## next } // namespace PrePostCondition } // namespace util #endif #define PRECONDITION(condition) UTIL_PRECONDITION( util::PrePostCondition::Print_ODS, util::PrePostCondition::Fail_Null, condition ) #define POSTCONDITION(condition) UTIL_POSTCONDITION( util::PrePostCondition::Print_ODS, util::PrePostCondition::Fail_Null, condition ) #define BEGIN_INVARIANT UTIL_BEGIN_INVARIANT #define DEFINE_INVARIANT UTIL_DEFINE_INVARIANT #define END_INVARIANT UTIL_END_INVARIANT #define POST UTIL_POST #define PRE UTIL_PRE /* //----------------------------------------- // trivial usage example #define PRECONDITION(condition) UTIL_PRECONDITION( util::PrePostCondition::Print_ODS, util::PrePostCondition::Fail_Null, condition ) #define POSTCONDITION(condition) UTIL_POSTCONDITION( util::PrePostCondition::Print_ODS, util::PrePostCondition::Fail_Null, condition ) #define BEGIN_INVARIANT UTIL_BEGIN_INVARIANT #define DEFINE_INVARIANT UTIL_DEFINE_INVARIANT #define END_INVARIANT UTIL_END_INVARIANT #define POST UTIL_POST #define PRE UTIL_PRE namespace util { namespace PrePostCondition { namespace example { class X { public: X(float a) : a(a) { POSTCONDITION(1); } ~X() { PRECONDITION(1); } void div(float b) { PRECONDITION(b > 0)(b); POSTCONDITION( POST(a) == a/b )(POST(a))(a)(b); a = a/b; } private: float a; BEGIN_INVARIANT(X) DEFINE_INVARIANT(a>0)(a) END_INVARIANT() }; } // namespace example } // namespace PrePostCondition } // namespace util #undef PRECONDITION #undef POSTCONDITION #undef BEGIN_INVARIANT #undef DEFINE_INVARIANT #undef END_INVARIANT #undef POST #undef PRE // end trivial usage example //----------------------------------------- */ #endif //! INCLUDE_UTIL_PREPOSTCONDITION_HPP