OWLNext    7.0
Borland's Object Windows Library for the modern age
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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