1 // license:GPL-2.0+
2 // copyright-holders:Couriersud
3 
4 #include "pexception.h"
5 #include "pfmtlog.h"
6 
7 #include <cfenv>
8 #include <cfloat>
9 #include <iostream>
10 
11 #if (defined(__x86_64__) || defined(__i386__)) && defined(__linux__)
12 #define HAS_FEENABLE_EXCEPT     (1)
13 #else
14 #define HAS_FEENABLE_EXCEPT     (0)
15 #endif
16 
17 namespace plib {
18 
19 	//============================================================
20 	// terminate
21 	//============================================================
22 
terminate(const char * msg)23 	void terminate(const char *msg) noexcept
24 	{
25 		try
26 		{
27 			std::cerr << msg << "\n";
28 		}
29 		catch (...)
30 		{
31 			/* ignore */
32 		}
33 		std::terminate();
34 	}
35 
passert_fail(const char * assertion,const char * file,int lineno,const char * msg)36 	void passert_fail(const char *assertion, const char *file, int lineno, const char *msg) noexcept
37 	{
38 		try
39 		{
40 			std::cerr << file << ":" << lineno << ": ";
41 			if (msg != nullptr)
42 				std::cerr << msg << "\n";
43 			else
44 				std::cerr << "Assertion '" << assertion << "' failed.\n";
45 		}
46 		catch (...)
47 		{
48 			/* ignore */
49 		}
50 		std::terminate();
51 	}
52 
53 	//============================================================
54 	//  Exceptions
55 	//============================================================
56 
pexception(const pstring & text)57 	pexception::pexception(const pstring &text)
58 	: m_text(text)
59 	{
60 	}
61 
62 
file_e(const pstring & fmt,const pstring & filename)63 	file_e::file_e(const pstring &fmt, const pstring &filename)
64 		: pexception(pfmt(fmt)(filename))
65 	{
66 	}
67 
68 
file_open_e(const pstring & filename)69 	file_open_e::file_open_e(const pstring &filename)
70 		: file_e("File open failed: {}", filename)
71 	{
72 	}
73 
74 
file_read_e(const pstring & filename)75 	file_read_e::file_read_e(const pstring &filename)
76 		: file_e("File read failed: {}", filename)
77 	{
78 	}
79 
80 
file_write_e(const pstring & filename)81 	file_write_e::file_write_e(const pstring &filename)
82 		: file_e("File write failed: {}", filename)
83 	{
84 	}
85 
86 
null_argument_e(const pstring & argument)87 	null_argument_e::null_argument_e(const pstring &argument)
88 		: pexception(pfmt("Null argument passed: {}")(argument))
89 	{
90 	}
91 
92 
out_of_mem_e(const pstring & location)93 	out_of_mem_e::out_of_mem_e(const pstring &location)
94 		: pexception(pfmt("Out of memory: {}")(location))
95 	{
96 	}
97 
98 
fpexception_e(const pstring & text)99 	fpexception_e::fpexception_e(const pstring &text)
100 		: pexception(pfmt("Exception error: {}")(text))
101 	{
102 	}
103 
104 
105 	bool fpsignalenabler::m_enable = false; // NOLINT
106 
107 	//FIXME: mingw needs to be compiled with "-fnon-call-exceptions"
108 
fpsignalenabler(unsigned fpexceptions)109 	fpsignalenabler::fpsignalenabler(unsigned fpexceptions)
110 	{
111 	#if HAS_FEENABLE_EXCEPT
112 		if (m_enable)
113 		{
114 			int b = 0;
115 			if (fpexceptions & plib::FP_INEXACT) b = b | FE_INEXACT;
116 			if (fpexceptions & plib::FP_DIVBYZERO) b = b | FE_DIVBYZERO;
117 			if (fpexceptions & plib::FP_UNDERFLOW) b = b | FE_UNDERFLOW;
118 			if (fpexceptions & plib::FP_OVERFLOW) b = b | FE_OVERFLOW;
119 			if (fpexceptions & plib::FP_INVALID) b = b | FE_INVALID;
120 			if ((b & m_last_enabled) != b)
121 				m_last_enabled = feenableexcept(b);
122 		}
123 	#elif defined(_WIN32) && defined(_EM_INEXACT)
124 		if (m_enable)
125 		{
126 			int b = _EM_DENORMAL | _EM_INEXACT | _EM_ZERODIVIDE | _EM_UNDERFLOW | _EM_OVERFLOW | _EM_INVALID;
127 			if (fpexceptions & plib::FP_INEXACT) b &= ~_EM_INEXACT;
128 			if (fpexceptions & plib::FP_DIVBYZERO) b &= ~_EM_ZERODIVIDE;
129 			if (fpexceptions & plib::FP_UNDERFLOW) b &= ~_EM_UNDERFLOW;
130 			if (fpexceptions & plib::FP_OVERFLOW) b &= ~_EM_OVERFLOW;
131 			if (fpexceptions & plib::FP_INVALID) b &= ~_EM_INVALID;
132 			m_last_enabled = _controlfp(0, 0);
133 			_controlfp(b, _MCW_EM );
134 		}
135 	#else
136 		m_last_enabled = 0;
137 	#endif
138 	}
139 
~fpsignalenabler()140 	fpsignalenabler::~fpsignalenabler()
141 	{
142 	#if HAS_FEENABLE_EXCEPT
143 		if (m_enable)
144 		{
145 			fedisableexcept(FE_ALL_EXCEPT);  // Enable all floating point exceptions but FE_INEXACT
146 			feenableexcept(m_last_enabled);  // Enable all floating point exceptions but FE_INEXACT
147 		}
148 	#elif defined(_WIN32) && defined(_EM_INEXACT)
149 		if (m_enable)
150 		{
151 			_controlfp(m_last_enabled, _MCW_EM);
152 		}
153 	#endif
154 	}
155 
supported()156 	bool fpsignalenabler::supported()
157 	{
158 	#if HAS_FEENABLE_EXCEPT
159 		return true;
160 	#elif defined(_WIN32) && defined(_EM_INEXACT)
161 		return true;
162 	#else
163 		return false;
164 	#endif
165 	}
166 
global_enable(bool enable)167 	bool fpsignalenabler::global_enable(bool enable)
168 	{
169 		bool old = m_enable;
170 		m_enable = enable;
171 		return old;
172 	}
173 
174 
175 } // namespace plib
176