1 #ifndef SimTK_SimTKCOMMON_EXCEPTION_H_ 2 #define SimTK_SimTKCOMMON_EXCEPTION_H_ 3 4 /* -------------------------------------------------------------------------- * 5 * Simbody(tm): SimTKcommon * 6 * -------------------------------------------------------------------------- * 7 * This is part of the SimTK biosimulation toolkit originating from * 8 * Simbios, the NIH National Center for Physics-Based Simulation of * 9 * Biological Structures at Stanford, funded under the NIH Roadmap for * 10 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. * 11 * * 12 * Portions copyright (c) 2005-15 Stanford University and the Authors. * 13 * Authors: Michael Sherman * 14 * Contributors: * 15 * * 16 * Licensed under the Apache License, Version 2.0 (the "License"); you may * 17 * not use this file except in compliance with the License. You may obtain a * 18 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * 19 * * 20 * Unless required by applicable law or agreed to in writing, software * 21 * distributed under the License is distributed on an "AS IS" BASIS, * 22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 23 * See the License for the specific language governing permissions and * 24 * limitations under the License. * 25 * -------------------------------------------------------------------------- */ 26 27 #include "SimTKcommon/internal/common.h" 28 29 #include <string> 30 #include <iostream> 31 #include <exception> 32 #include <cstdarg> 33 #include <cstdio> 34 35 namespace SimTK { 36 37 namespace Exception { 38 39 // Keeps MS VC++ quiet about sprintf, strcpy, etc. 40 #ifdef _MSC_VER 41 #pragma warning(push) 42 #pragma warning(disable:4996) 43 #endif 44 45 // SimTK::Exception::Base 46 class Base : public std::exception { 47 public: 48 explicit Base(const char* fn="<UNKNOWN>", int ln=0) fileName(fn)49 : fileName(fn), lineNo(ln) { } ~Base()50 virtual ~Base() throw() { } getMessage()51 const std::string& getMessage() const { return msg; } getMessageText()52 const std::string& getMessageText() const { return text; } 53 54 // override virtual function from std::exception what()55 const char* what() const throw() override {return getMessage().c_str();} 56 protected: setMessage(const std::string & msgin)57 void setMessage(const std::string& msgin) { 58 text = msgin; 59 msg = "SimTK Exception thrown at " + where() + ":\n " + msgin; 60 } 61 private: 62 std::string fileName; // where the exception was thrown 63 int lineNo; 64 std::string msg; // a message formatted for display by catcher 65 std::string text; // the original passed-in text 66 shortenFileName(const std::string & fn)67 static std::string shortenFileName(const std::string& fn) 68 { std::string::size_type pos = fn.find_last_of("/\\"); 69 if (pos+1>=fn.size()) pos=0; 70 return std::string(fn,(int)(pos+1),(int)(fn.size()-(pos+1))); 71 } 72 where()73 std::string where() const { 74 char buf[32]; 75 sprintf(buf,"%d",lineNo); 76 return shortenFileName(fileName) + ":" + std::string(buf); 77 } 78 }; 79 80 /// This is for reporting internally-detected bugs only, not problems induced by 81 /// confused users (that is, it is for confused developers instead). The exception 82 /// message accepts printf-style arguments and should contain lots of useful 83 /// information for developers. Don't throw 84 /// this exception directly; use one of the family of SimTK_ASSERT and 85 /// SimTK_ASSERT_ALWAYS macros. 86 class Assert : public Base { 87 public: Assert(const char * fn,int ln,const char * assertion,const char * fmt...)88 Assert(const char* fn, int ln, const char* assertion, 89 const char* fmt ...) : Base(fn,ln) 90 { 91 char buf[1024]; 92 va_list args; 93 va_start(args, fmt); 94 vsprintf(buf, fmt, args); 95 96 setMessage("Internal bug detected: " + std::string(buf) 97 + "\n (Assertion '" + std::string(assertion) + "' failed).\n" 98 " Please file an Issue at https://github.com/simbody/simbody/issues.\n" 99 " Include the above information and anything else needed to reproduce the problem."); 100 va_end(args); 101 } ~Assert()102 virtual ~Assert() throw() { } 103 }; 104 105 /// This is for reporting errors occurring during execution of SimTK core methods, 106 /// beyond those caused by mere improper API arguments, which should be reported with 107 /// APIArgcheck instead. Nor is this intended for detection of internal 108 /// bugs; use Assert instead for that. It is expected that this error resulted from 109 /// something the API user did, so the messages should be suitable for reporting to 110 /// that programmer. The exception message accepts printf-style arguments and should 111 /// contain lots of useful information for the API user. Don't throw this exception 112 /// directly; use one of the family SimTK_ERRCHK and SimTK_ERRCHK_ALWAYS macros. 113 class ErrorCheck : public Base { 114 public: ErrorCheck(const char * fn,int ln,const char * assertion,const char * whereChecked,const char * fmt...)115 ErrorCheck(const char* fn, int ln, const char* assertion, 116 const char* whereChecked, // e.g., ClassName::methodName() 117 const char* fmt ...) : Base(fn,ln) 118 { 119 char buf[1024]; 120 va_list args; 121 va_start(args, fmt); 122 vsprintf(buf, fmt, args); 123 124 setMessage("Error detected by Simbody method " 125 + std::string(whereChecked) + ": " 126 + std::string(buf) 127 + "\n (Required condition '" + std::string(assertion) + "' was not met.)\n"); 128 va_end(args); 129 } ~ErrorCheck()130 virtual ~ErrorCheck() throw() { } 131 }; 132 133 /// This is for reporting problems detected by checking the caller's supplied arguments 134 /// to a SimTK API method. Messages should be suitable for SimTK API users. This is not 135 /// intended for detection of internal bugs where a SimTK developer passed bad arguments 136 /// to some internal routine -- use Assert instead for that. The exception message 137 /// accepts printf-style arguments and should contain useful information for the API user. 138 /// Don't throw this exception directly; use one of the family SimTK_APIARGCHECK and 139 /// SimTK_APIARGCHECK_ALWAYS macros. 140 class APIArgcheckFailed : public Base { 141 public: APIArgcheckFailed(const char * fn,int ln,const char * assertion,const char * className,const char * methodName,const char * fmt...)142 APIArgcheckFailed(const char* fn, int ln, const char* assertion, 143 const char* className, const char* methodName, 144 const char* fmt ...) : Base(fn,ln) 145 { 146 char buf[1024]; 147 va_list args; 148 va_start(args, fmt); 149 vsprintf(buf, fmt, args); 150 setMessage("Bad call to Simbody API method " 151 + std::string(className) + "::" + std::string(methodName) + "(): " 152 + std::string(buf) 153 + "\n (Required condition '" + std::string(assertion) + "' was not met.)"); 154 va_end(args); 155 } ~APIArgcheckFailed()156 virtual ~APIArgcheckFailed() throw() { } 157 }; 158 159 160 class IndexOutOfRange : public Base { 161 public: IndexOutOfRange(const char * fn,int ln,const char * indexName,long long lb,long long index,long long ub,const char * where)162 IndexOutOfRange(const char* fn, int ln, const char* indexName, 163 long long lb, long long index, long long ub, const char* where) 164 : Base(fn,ln) 165 { 166 char buf[1024]; 167 168 sprintf(buf, "Index out of range in %s: expected %lld <= %s < %lld but %s=%lld.", 169 where,lb,indexName,ub,indexName,index); 170 setMessage(std::string(buf)); 171 } ~IndexOutOfRange()172 virtual ~IndexOutOfRange() throw() { } 173 }; 174 175 class SizeOutOfRange : public Base { 176 public: SizeOutOfRange(const char * fn,int ln,const char * szName,unsigned long long sz,unsigned long long maxsz,const char * where)177 SizeOutOfRange(const char* fn, int ln, const char* szName, 178 unsigned long long sz, unsigned long long maxsz, const char* where) 179 : Base(fn,ln) 180 { 181 char buf[1024]; 182 183 sprintf(buf, "Size out of range in %s: expected 0 <= %s <= %llu but %s=%llu.", 184 where,szName,maxsz,szName,sz); 185 setMessage(std::string(buf)); 186 } ~SizeOutOfRange()187 virtual ~SizeOutOfRange() throw() { } 188 }; 189 190 class SizeWasNegative : public Base { 191 public: SizeWasNegative(const char * fn,int ln,const char * szName,unsigned long long sz,const char * where)192 SizeWasNegative(const char* fn, int ln, const char* szName, 193 unsigned long long sz, const char* where) 194 : Base(fn,ln) 195 { 196 char buf[1024]; 197 198 sprintf(buf, "Size argument was negative in %s: expected 0 <= %s but %s=%llu.", 199 where,szName,szName,sz); 200 setMessage(std::string(buf)); 201 } ~SizeWasNegative()202 virtual ~SizeWasNegative() throw() { } 203 }; 204 205 class ValueOutOfRange : public Base { 206 public: ValueOutOfRange(const char * fn,int ln,const char * valueName,double lowerBound,double value,double upperBound,const char * where)207 ValueOutOfRange(const char* fn, int ln, const char* valueName, 208 double lowerBound, double value, double upperBound, 209 const char* where) 210 : Base(fn,ln) 211 { 212 char buf[1024]; 213 214 sprintf(buf, "Value out of range in %s: expected %g <= %s <= %g but %s=%g.", 215 where,lowerBound,valueName,upperBound,valueName,value); 216 setMessage(std::string(buf)); 217 } ~ValueOutOfRange()218 virtual ~ValueOutOfRange() throw() { } 219 }; 220 221 class ValueWasNegative : public Base { 222 public: ValueWasNegative(const char * fn,int ln,const char * valueName,double value,const char * where)223 ValueWasNegative(const char* fn, int ln, const char* valueName, 224 double value, const char* where) 225 : Base(fn,ln) 226 { 227 char buf[1024]; 228 229 sprintf(buf, "Expected non-negative value for %s in %s but got %g.", 230 valueName,where,value); 231 setMessage(std::string(buf)); 232 } ~ValueWasNegative()233 virtual ~ValueWasNegative() throw() { } 234 }; 235 236 class UnimplementedMethod : public Base { 237 public: UnimplementedMethod(const char * fn,int ln,std::string methodName)238 UnimplementedMethod(const char* fn, int ln, std::string methodName) 239 : Base(fn,ln) 240 { 241 setMessage("The method " + methodName 242 + "is not yet implemented. Please post to the Simbody forum" 243 " to find a workaround or request implementation."); 244 } ~UnimplementedMethod()245 virtual ~UnimplementedMethod() throw() { } 246 }; 247 248 class UnimplementedVirtualMethod : public Base { 249 public: UnimplementedVirtualMethod(const char * fn,int ln,std::string baseClass,std::string methodName)250 UnimplementedVirtualMethod(const char* fn, int ln, 251 std::string baseClass, std::string methodName) 252 : Base(fn,ln) 253 { 254 setMessage("The base class " + baseClass + 255 " dummy implementation of method " + methodName 256 + "() was invoked because a derived class did not provide an implementation."); 257 } ~UnimplementedVirtualMethod()258 virtual ~UnimplementedVirtualMethod() throw() { } 259 }; 260 261 class IncompatibleValues : public Base { 262 public: IncompatibleValues(const char * fn,int ln,std::string src,std::string dest)263 IncompatibleValues(const char* fn, int ln, std::string src, std::string dest) : Base(fn,ln) 264 { 265 setMessage("Attempt to assign a Value<"+src+"> to a Value<"+dest+">"); 266 } ~IncompatibleValues()267 virtual ~IncompatibleValues() throw() { } 268 }; 269 270 class OperationNotAllowedOnView : public Base { 271 public: OperationNotAllowedOnView(const char * fn,int ln,const std::string & op)272 OperationNotAllowedOnView(const char* fn, int ln, const std::string& op) : Base(fn,ln) 273 { 274 setMessage("Operation '" + op + "' allowed only for owners, not views"); 275 } ~OperationNotAllowedOnView()276 virtual ~OperationNotAllowedOnView() throw() { } 277 }; 278 279 class OperationNotAllowedOnOwner : public Base { 280 public: OperationNotAllowedOnOwner(const char * fn,int ln,const std::string & op)281 OperationNotAllowedOnOwner(const char* fn, int ln, const std::string& op) : Base(fn,ln) 282 { 283 setMessage("Operation '" + op + "' allowed only for views, not owners"); 284 } ~OperationNotAllowedOnOwner()285 virtual ~OperationNotAllowedOnOwner() throw() { } 286 }; 287 288 class OperationNotAllowedOnNonconstReadOnlyView : public Base { 289 public: OperationNotAllowedOnNonconstReadOnlyView(const char * fn,int ln,const std::string & op)290 OperationNotAllowedOnNonconstReadOnlyView(const char* fn, int ln, const std::string& op) : Base(fn,ln) 291 { 292 setMessage("Operation '" + op + "' not allowed on non-const readonly view"); 293 } ~OperationNotAllowedOnNonconstReadOnlyView()294 virtual ~OperationNotAllowedOnNonconstReadOnlyView() throw() { } 295 }; 296 297 // SimTK::Exception::Cant 298 class Cant : public Base { 299 public: Cant(const char * fn,int ln,const std::string & s)300 Cant(const char* fn, int ln, const std::string& s) : Base(fn,ln) 301 { 302 setMessage("Can't perform operation: " + s); 303 } ~Cant()304 virtual ~Cant() throw() { } 305 }; 306 307 #ifdef _MSC_VER 308 #pragma warning(pop) 309 #endif 310 311 } // namespace Exception 312 } // namespace SimTK 313 314 #define SimTK_THROW(exc) \ 315 throw exc(__FILE__, __LINE__) 316 #define SimTK_THROW1(exc,a1) \ 317 throw exc(__FILE__, __LINE__,a1) 318 #define SimTK_THROW2(exc,a1,a2) \ 319 throw exc(__FILE__, __LINE__,a1,a2) 320 #define SimTK_THROW3(exc,a1,a2,a3) \ 321 throw exc(__FILE__, __LINE__,a1,a2,a3) 322 #define SimTK_THROW4(exc,a1,a2,a3,a4) \ 323 throw exc(__FILE__, __LINE__,a1,a2,a3,a4) 324 #define SimTK_THROW5(exc,a1,a2,a3,a4,a5) \ 325 throw exc(__FILE__, __LINE__,a1,a2,a3,a4,a5) 326 #define SimTK_THROW6(exc,a1,a2,a3,a4,a5,a6) \ 327 throw exc(__FILE__, __LINE__,a1,a2,a3,a4,a5,a6) 328 #define SimTK_THROW7(exc,a1,a2,a3,a4,a5,a6,a7) \ 329 throw exc(__FILE__, __LINE__,a1,a2,a3,a4,a5,a6,a7) 330 #define SimTK_THROW8(exc,a1,a2,a3,a4,a5,a6,a7,a8) \ 331 throw exc(__FILE__, __LINE__,a1,a2,a3,a4,a5,a6,a7,a8) 332 #define SimTK_THROW9(exc,a1,a2,a3,a4,a5,a6,a7,a8,a9) \ 333 throw exc(__FILE__, __LINE__,a1,a2,a3,a4,a5,a6,a7,a8,a9) 334 #define SimTK_THROW10(exc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) \ 335 throw exc(__FILE__, __LINE__,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) 336 337 #endif // SimTK_SimTKCOMMON_EXCEPTION_H_ 338 339