OWLNext    7.0
Borland's Object Windows Library for the modern age
Loading...
Searching...
No Matches
checks.cpp
Go to the documentation of this file.
1//----------------------------------------------------------------------------
2// ObjectWindows
3// Copyright (C) 1993, 1996 by Borland International, All Rights Reserved
4// Copyright (C) 1998 by Yura Bidus
5//----------------------------------------------------------------------------
6
7#if defined(BI_COMP_BORLANDC)
8# pragma hdrstop
9#endif
10
11#define CHECKS_CPP
12
13//
14// Make sure __TRACE is defined so that we can provide
15// run-time support in non-debug versions of the library.
16//
17#if defined(__TRACE)
18# define __TRACE_PREVIOUSLY_DEFINED
19#endif
20
21#undef __TRACE
22#define __TRACE
23
24#if !defined(__TRACE_PREVIOUSLY_DEFINED)
25#define _DONT_DECLARE_DEF_DIAG_GROUP // We need this define so checks.h won't try to declare Def diag group.
26#endif
27
28#include <owl/defs.h>
29
30#if defined(BI_COMP_BORLANDC)
31# pragma option -w-inl // Disable warning "Functions containing 'statement' is not expanded inline".
32#endif
33
34#include <owl/private/checks.h>
35
36#if defined(UNICODE)
37# if defined(BI_COMP_GNUC)
38# include <stdlib.h>
39# else
40# include <malloc.h>
41# endif
42#endif
43
44#if defined(BI_MULTI_THREAD_RTL)
45#include <owl/thread.h>
46#endif
47
48#include <owl/module.h>
49
50using namespace std;
51namespace owl {
52
53//
54// Definition of the default diagnostic group "Def" (expands to TDiagGroupDef)
55//
56#if defined(_BUILDOWLDLL)
57# if defined(__DEBUG) && defined(__TRACE_PREVIOUSLY_DEFINED)
59# else
61# endif
62#else
63# if defined(__DEBUG) && defined(__TRACE_PREVIOUSLY_DEFINED)
65# else
67# endif
68#endif
69
70struct TDiagProcessData
71{
72 TDiagBaseHook* GlobalHook;
73 TDiagBase* DiagGroupStaticHead;
74
75private:
76
77 TDiagProcessData() {}
78 TDiagProcessData(const TDiagProcessData&); // forbidden
79 TDiagProcessData& operator =(const TDiagProcessData&); // forbidden
80
81 static TDiagProcessData& GetInstance()
82 {
83 //
84 // Note that while this lazy initialization avoids problems with global initialization order,
85 // the initial call of this function is not thread-safe (pre C++11). This should not be a
86 // problem here because diagnostics groups are initialized during program start-up, at which
87 // time only the main thread should be running. But, as a safe-guard, we ensure this function
88 // is called during program start-up using InitDiagProcessDataInstance below.
89 //
90 // This safe-guard can be removed when C++11 compliant compilers are mandated.
91 //
92 // Also note that the singleton construction may throw an exception. Since there is no way to
93 // continue without this singleton, we make no attempt to handle it here. We assume that
94 // the exception will terminate the program, or if it is handled, that subsequent calls will
95 // be retried still within a single thread.
96 //
97 static TDiagProcessData d; // initial call (construction) not thread-safe pre-C++11
98 return d;
99 }
100
101 static TDiagProcessData& InitDiagProcessDataInstance;
102
103public:
104
105#if defined(BI_MULTI_THREAD_RTL)
106
107 template <class TData, bool Shared>
108 class TLockT
109 : public TMRSWSection::TLock
110 {
111 public:
112
113 TData& Data;
114
115 explicit TLockT(TDiagProcessData& data = TDiagProcessData::GetInstance())
116 : TMRSWSection::TLock(data.Section, Shared, true), // wait
117 Data(data)
118 {}
119 };
120
122 typedef TLockT<const TDiagProcessData, true> TConstLock;
123
124 template <class TData, bool Shared>
125 friend class TLockT;
126
127private:
128
129 TMRSWSection Section;
130
131#else
132
133 template <class TData>
134 class TLockT
135 {
136 public:
137
138 TData& Data;
139
140 explicit TLockT(TDiagProcessData& data = TDiagProcessData::GetInstance())
141 : Data(data)
142 {}
143 };
144
145 typedef TLockT<TDiagProcessData> TLock;
146 typedef TLockT<const TDiagProcessData> TConstLock;
147
148 template <class TData>
149 friend class TLockT;
150
151#endif
152
153};
154
155//
156// Ensure singleton initialization at start-up (single-threaded, safe).
157//
158TDiagProcessData& TDiagProcessData::InitDiagProcessDataInstance = TDiagProcessData::GetInstance();
159
160void
162{
163 Message("Trace", msg, level, fname, line);
164}
165
166void
168{
169 Message("Warning", msg, level, fname, line);
170}
171
174{
176 TDiagBaseHook* oldHook = lock.Data.GlobalHook;
177 lock.Data.GlobalHook = hook;
178 return oldHook;
179}
180
182:
183 Name(name),
184 Enabled(enable),
185 Level(level),
186 LocalHook(nullptr),
187 NextGroup{nullptr}
188{
189 AddDiagGroup(this);
190}
191
193{
194 RemoveDiagGroup(this);
195}
196
197void
198TDiagBase::AddDiagGroup(TDiagBase* group)
199{
201 if (lock.Data.DiagGroupStaticHead == nullptr)
202 lock.Data.DiagGroupStaticHead = group;
203 else
204 {
205 TDiagBase* last = lock.Data.DiagGroupStaticHead;
206 while (last->NextGroup)
207 last = last->NextGroup;
208 last->NextGroup = group;
209 }
210}
211
212void
213TDiagBase::RemoveDiagGroup(TDiagBase* group)
214{
215 TDiagProcessData::TLock lock;
216 if (lock.Data.DiagGroupStaticHead == group)
217 lock.Data.DiagGroupStaticHead = group->NextGroup;
218 else
219 {
220 TDiagBase* last = lock.Data.DiagGroupStaticHead;
221 while (last->NextGroup != group)
222 last = last->NextGroup;
223 last->NextGroup = group->NextGroup;
224 }
225}
226
227//
228// NOTE: To access the linked list of diagnostics groups in a thread-safe manner, the caller of
229// this function should ideally lock the structure for the duration of the access, since in theory
230// other threads may otherwise mutate the linked list. In the current design, this is not possible
231// since access to TDiagBase::NextGroup is not synchronized. In practice, the mutation of the
232// linked list only happens at startup and shutdown, so this problem should not be an issue.
233//
234TDiagBase*
236{
237 if(curr)
238 return curr->NextGroup;
239
241 return lock.Data.DiagGroupStaticHead;
242}
243
244//
245// Sends the specified message to the debug output device.
246// If a global or local hook is registered, then it is used to output the string.
247// Otherwise, the system debug output function is used.
248//
249void
250TDiagBase::Output(const tstring& msg)
251{
252 {
254 if (lock.Data.GlobalHook)
255 {
256 lock.Data.GlobalHook->Output(this, msg.c_str());
257 return;
258 }
259 }
260 if (LocalHook)
261 LocalHook->Output(this, msg.c_str());
262 else
263 {
265 }
266}
267
268void
269TDiagBase::Message(LPCSTR type, const tstring& msg, int level, LPCSTR file, int line)
270{
273#if defined(BI_COMP_MSC)
274 out << _A2W(file) << _T("(");
275 out << line << _T(") : ") << _A2W(type)
276#else
277 out << _A2W(type) << _T(" ");
278 out << _A2W(file) << _T(" ") << line
279#endif
280 << _T(": [") << Name << _T(':') << level << _T("] ")
281 << msg
282 << _T("\r\n");
283
284 Output(out.str());
285}
286
287static LONG TraceIndent = 0;
288
290:
291 Group(group), Level(level), Function(function), File(file), Line(line),
292 Enabled(group.IsEnabled() && level <= group.GetLevel())
293{
294 if (Enabled)
295 {
296 Trace(_T(">> "), nullptr);
297 InterlockedIncrement(&TraceIndent);
298 }
299}
300
302:
303 Group(group), Level(level), Function(function), File(file), Line(line),
304 Enabled(group.IsEnabled() && level <= group.GetLevel())
305{
306 if (Enabled)
307 {
308 Trace(_T(">> "), params);
309 InterlockedIncrement(&TraceIndent);
310 }
311}
312
314{
315 if (Enabled)
316 {
317 InterlockedDecrement(&TraceIndent);
318 Trace(_T("<< "), nullptr);
319 }
320}
321
322void
323TDiagFunction::Trace(LPCTSTR prefix, LPCTSTR params)
324{
326 for (int indent = TraceIndent; indent--;)
327 out << _T(" ");
328 out << prefix << Function;
329 if (params)
330 out << _T(" (") << params << _T(")");
331 Group.Trace(out.str().c_str(), Level, File, Line);
332}
333
334namespace
335{
336 tstring
337 MakeString(LPCSTR type, const tstring& msg, LPCSTR file, int line)
338 {
341 s << _A2W(type) << _T(" failed in \"");
342 s << _A2W(file) << _T("\" at line ") << line << _T(": ") << msg;
343 return s.str();
344 }
345}
346
348:
349 std::exception(),
350 delegate(MakeString(type, msg, file, line))
351{}
352
353const char*
355{
356 return delegate.what();
357}
358
361{
362 return delegate.why();
363}
364
371
372int
374{
376
377 static LONG entranceCount = 0;
378 struct TReentranceGuard
379 {
382 }
383 guard;
384
385 tstring error = MakeString(type, msg, file, line);
386 if (entranceCount > 1)
387 {
388 // Reentrance; send error message to debugger or auxiliary port instead.
389 // Then assert within assert (examine call stack to determine first one).
390 //
391 OutputDebugString(error.c_str());
393 return 3;
394 }
395
396 // Get the active popup window for the current thread.
397 //
398 HWND hParent = ::GetActiveWindow();
399 if (hParent)
400 {
401 HWND hPopup = ::GetLastActivePopup(hParent);
402 if (hPopup)
403 hParent = hPopup;
404 }
405
406 // Temporarily remove any WM_QUIT message in the queue,
407 // otherwise the message box will not display.
408 //
409 struct TQuitMsgHider
410 {
411 MSG QuitMsg;
412 bool DidRemoveQuitMsg;
414 ~TQuitMsgHider() {if (DidRemoveQuitMsg) PostQuitMessage(static_cast<int>(QuitMsg.wParam));}
415 }
416 qmh;
417
418 error += _T("\n\nSelect Abort to terminate, Retry to debug, or Ignore to continue.");
419 int ret = ::MessageBox(hParent, error.c_str(), _A2W(type),
421 return ret;
422}
423
425: TDiagException("Precondition", msg, file, line)
426{}
427
429: TDiagException("Check", msg, file, line)
430{}
431
432} // OWL namespace
#define OWL_OUTPUT_DEBUG_STRING(lpsz)
Definition borlandc.h:51
#define OWL_BREAK
Definition borlandc.h:49
Diagnostic macros for assertions and tracing.
#define OWL_DIAG_DEFINE_GROUP_INIT(f, g, e, l)
Definition checks.h:426
#define OWL_DIAG_DEFINE_EXPORTGROUP_INIT(f, g, e, l)
Definition checks.h:432
#define DIAG_DEFINE_GROUP_INIT(f, g, e, l)
Definition checks.h:429
#define DIAG_DEFINE_EXPORTGROUP_INIT(f, g, e, l)
Definition checks.h:435
TCheckFailure(const tstring &msg, LPCSTR file, int line)
Definition checks.cpp:428
void Trace(const tstring &msg, int level, LPCSTR file, int line)
Definition checks.cpp:161
TDiagBaseHook * LocalHook
Definition checks.h:83
static TDiagBase * GetDiagGroup(TDiagBase *curr=nullptr)
Definition checks.cpp:235
LPCSTR Name
Definition checks.h:80
TDiagBase(LPCSTR name, bool enable, int level)
Definition checks.cpp:181
virtual ~TDiagBase()
Definition checks.cpp:192
static TDiagBaseHook * SetGlobalHook(TDiagBaseHook *hook=nullptr)
Definition checks.cpp:173
void Warn(const tstring &msg, int level, LPCSTR file, int line)
Definition checks.cpp:167
static tstring ToString(LPCSTR s)
Definition checks.cpp:366
virtual const char * what() const noexcept
Definition checks.cpp:354
static int BreakMessage(LPCSTR type, const tstring &msg, LPCSTR file, int line)
Definition checks.cpp:373
virtual tstring why() const
Definition checks.cpp:360
TDiagException(LPCSTR type, const tstring &msg, LPCSTR file, int line)
Definition checks.cpp:347
TDiagFunction(TDiagBase &group, int level, LPCTSTR function, LPCSTR file, int line)
Definition checks.cpp:289
friend class TMRSWSection::TLock
Definition thread.h:376
TPreconditionFailure(const tstring &msg, LPCSTR file, int line)
Definition checks.cpp:424
const char * what() const noexcept
Definition exbase.cpp:155
tstring why() const
Definition exbase.cpp:169
#define _T(x)
Definition cygwin.h:51
#define _A2W(lpw)
Definition memory.h:220
#define _USES_CONVERSION
Definition memory.h:217
Definition of class TModule.
Object Windows Library (OWLNext Core)
Definition animctrl.h:22
std::ostringstream tostringstream
Definition strmdefs.h:36
const tchar * Section
Definition rcntfile.cpp:45
std::string tstring
Definition defs.h:79
General definitions used by all ObjectWindows programs.
#define OWL_INI
Definition defs.h:170
virtual void Output(TDiagBase *group, LPCTSTR str)=0