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