Re: Are throwing default constructors bad style, and if so, why?
On Oct 21, 8:37 am, Marsh Ray <marsh...@gmail.com> wrote:
Mods: Sorry if this is too specific to a compiler, please bounce it if
so. The goal is to get the great-grandparent poster onto standard
idioms.
{ Accepted as a follow-up. Posters are advised that further environment-
specific discussions are better directed to other groups. -mod }
On Oct 16, 10:14 pm, David Abrahams <d...@boostpro.com> wrote:
Then it's a bad platform, because unless something has changed in the
past few years, all Windows 32-bit compilers still use a
setjmp/longjmp-like implementation of EH so they can interoperate with
"structured exception handling." It's usually not a particularly good
idea to interoperate that way, but that's the norm on 32-bit Windows.
Metrowerks, when they still existed, used to offer an option to do EH
the fast way or the interoperable way, but they're long gone now :(.
...
Got any suggestions? For now at least, 32-bit Windows is still an
important platform to support.
Have you looked into MSVC's /EHsc compiler setting instead of /EHa?
It seems to allow the compiler to disregard any exceptions from non-C+
+ code, so 'catch (...)' no longer catches general SEH exceptions.
- Marsh
{ clc++m banner removed; don't quote it. -mod }
Mods: Same as Marsh Ray. I'm just replying to his platform specific
question to answer what's a good platform independent style given how
the language is commonly implemented vs how it is intended. I'm not
sure how much detail I should put, or exactly what would constitute a
good post for this newsgroup. Sorry if I'm crossing some lines here.
**Compiler version
[Irrelevant portions snipped]
Microsoft Visual Studio 2005
Version 8.0.50727.762 (SP.050727-7600)
Microsoft .NET Framework
Version 2.0.50727
Installed Edition: Professional
Microsoft Visual C++ 2005 77626-009-0000007-41148
Microsoft Visual C++ 2005
Microsoft Visual Studio 2005 Professional Edition - ENU Service Pack 1
(KB926601)
This service pack is for Microsoft Visual Studio 2005 Professional
Edition - ENU.
If you later install a more recent service pack, this service pack
will be uninstalled automatically.
For more information, visit http://support.microsoft.com/kb/926601
**Compiler options:
/O2 /Ob2 /Ot /Oy /GL /GF /FD /EHsc /MT /GS- /Za /Fo"Release\\" /
Fd"Release\vc80.pdb" /nologo /c /TP /errorReport:prompt
**Linker options:
/OUT:"C:\Documents and Settings\Vash the Stampede\Desktop\cpptester
\Release\cpptester.exe" /NOLOGO /MANIFEST /MANIFESTFILE:"Release
\cpptester.exe.intermediate.manifest" /OPT:REF /OPT:ICF /LTCG /
ERRORREPORT:PROMPT kernel32.lib user32.lib gdi32.lib winspool.lib
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib
odbc32.lib odbccp32.lib
DIR>cpptester.exe 60000 -10
Virtual Return Code Fake Try : 29.218
Manually Inlined Return Code : 5.875
Virtual Exception : 23.469
Inlineable Global Return Code : 7.781
Inlineable Member Return Code : 5.859
Virtual Return Code : 24.625
Manually Inlined Optimized Return Code : 5.985
DIR>cpptester.exe 60000 -10
Inlineable Member Return Code : 5.968
Virtual Exception : 23.469
Inlineable Global Return Code : 7.766
Manually Inlined Return Code : 5.828
Virtual Return Code : 24.641
Virtual Return Code Fake Try : 29.203
Manually Inlined Optimized Return Code : 5.938
I've run it on AIX 32, 64, HP 32, 64, HP IPF, Linux 32, 64, Sun 64,
Sun x86, Win 32, a.k.a. all the systems I have access to at work, a
subset of the systems which my company supports.
AIX with xlC_r version 6 seems to handle it well, either with no
overhead or very small overhead.
- On HP 32 with aCC B3910B A.03.73, Virtual Exception is slower than
Virtual Fake Try is roughly equal to Virtual Return Code.
- On HP 64 with aCC B3910B A.03.73, Virtual Exception is slower than
Virtual Fake Try is slower than Virtual Return Code. (This is the
worst offender I've found: the Virtual Exception test is ~40% slower
than the Virtual Return Code test.)
- On HP IPF with aCC B3910B A.06.05, Virtual Exception is slower than
Virtual Fake Try is roughly equal to Virtual Return Code.
gcc 3.4.3 on Linux 32 and 64 does exactly as you would expect. All 4
inlineable / inlined tests run for the same time within .01 seconds of
precision, and all 3 virtual tests run for the same time within .01
seconds of precision. This compiler gave me the best results of the
compilers which I tested.
- Sun 64 with CC 5.5 either has no overhead or very small overhead.
- Sun on x86 runs slightly, but statistically significantly, ~5%
slower on the Virtual Fake Try test than the other two virtual tests.
I do not believe the 40% or the 5% is meaningful in any way, except to
say there is overhead. Trying to compare overhead between
implementations which have overhead or trying to compare how much
overhead there is vs a no overhead system is probably a bad idea from
this data. All I believe you can safely take away from these tests is
that "There is speed overhead even if an exception is never thrown" or
"There is no speed overhead when exceptions are not thrown".
I used the following code for the tests. I do not know enough if this
is a valid test case. I do not know what I should be looking for. I am
not well versed in what can throw numbers off, in what can give
artificial results for tests. I'd like to think the test is a halfway
decent.
(Note that a throw() specification may help out the windows and sun
x86 cases. In contradiction with the standard, the visual studios
treats a throw() specification as the user telling the compiler it
will not throw, allowing the compiler to optimize accordingly (or so
I've been told).)
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
enum ReturnCodeT { Success = 1, Failure = 2 };
class TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum) = 0;
virtual ReturnCodeT virtualReturnCode(int a, int b, int targetSum)
= 0;
ReturnCodeT inlineableReturnCode(int a, int b, int targetSum)
{ if (a + b == targetSum)
return Failure;
return Success;
}
};
inline ReturnCodeT globalInlineableReturnCode(int a, int b, int
targetSum)
{ if (a + b == targetSum)
return Failure;
return Success;
}
class TestImpl : public TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum)
{ if (a + b == targetSum)
throw 1;
}
virtual ReturnCodeT virtualReturnCode(int a, int b, int targetSum)
{ if (a + b == targetSum)
return Failure;
return Success;
}
};
class TestImpl2 : public TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum)
{ cout << "XXX" << endl;
}
virtual ReturnCodeT virtualReturnCode(int a, int b, int targetSum)
{ cout << "XXX" << endl;
return Success;
}
};
void testInlineableMemberReturnCode(TestInterface *& x, int arg1, int
arg2)
{ for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
if (Failure == x->inlineableReturnCode(i, j, arg2+3))
{ cout << "inlineable member return code, returned
failure" << endl;
x = new TestImpl2();
}
}
void testInlineableGlobalReturnCode(TestInterface *& x, int arg1, int
arg2)
{ for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
if (Failure == globalInlineableReturnCode(i, j, arg2+4))
cout << "inlineable global return code, returned
failure" << endl;
}
void testVirtualReturnCodeFakeTry(TestInterface *& x, int arg1, int
arg2)
{ for (int i=0; i<arg1; ++i)
{ for (int j=0; j<arg1; ++j)
{ try
{ if (Failure == x->virtualReturnCode(i, j, arg2+5))
{ cout << "virtual return code with fake try,
returned failure" << endl;
x = new TestImpl2();
}
} catch (...)
{ cout << "ERROR impossible exception caught" << endl;
x = new TestImpl2();
}
}
}
}
void testVirtualReturnCode(TestInterface *& x, int arg1, int arg2)
{ for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
if (Failure == x->virtualReturnCode(i, j, arg2+6))
{ cout << "virtual return code, returned failure" <<
endl;
x = new TestImpl2();
}
}
void testVirtualException(TestInterface *& x, int arg1, int arg2)
{ for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
{ try
{ x->virtualCanThrow(i, j, arg2+7);
} catch (int & )
{ cout << "virtual exception, exception caught" << endl;
x = new TestImpl2();
}
}
}
void testManuallyInlinedReturnCode(TestInterface *& x, int arg1, int
arg2)
{ for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
{ ReturnCodeT retVal;
if (i + j == arg2+8)
retVal = Failure;
else
retVal = Success;
if (retVal == Failure)
{ cout << "manually inlined return code, failure
'returned'" << endl;
x = new TestImpl2();
}
}
}
void testManuallyInlinedOptimizedReturnCode(TestInterface *& x, int
arg1, int arg2)
{ for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
if (i + j == arg2+9)
{ cout << "manually inlined and optimized return code,
failure 'returned'" << endl;
x = new TestImpl2();
}
}
int main(int argc, char ** argv)
{
if (argc != 3 && argc != 4)
return 1;
int arg1;
if (! (stringstream(argv[1]) >> arg1))
return 1;
int arg2;
if (! (stringstream(argv[2]) >> arg2))
return 1;
TestInterface * x;
if (argc == 3)
x = new TestImpl();
else
x = new TestImpl2();
typedef void (*TestFuncType)(TestInterface *&, int, int);
vector<pair<string, TestFuncType> > remainingTests;
remainingTests.push_back(make_pair(string("Inlineable Member
Return Code"), & testInlineableMemberReturnCode));
remainingTests.push_back(make_pair(string("Inlineable Global
Return Code"), & testInlineableGlobalReturnCode));
remainingTests.push_back(make_pair(string("Virtual Return Code
Fake Try"), & testVirtualReturnCodeFakeTry));
remainingTests.push_back(make_pair(string("Virtual Return
Code"), & testVirtualReturnCode));
remainingTests.push_back(make_pair(string("Virtual
Exception"), & testVirtualException));
remainingTests.push_back(make_pair(string("Manually Inlined Return
Code"), & testManuallyInlinedReturnCode));
remainingTests.push_back(make_pair(string("Manually Inlined
Optimized Return Code"), & testManuallyInlinedOptimizedReturnCode));
srand(time(0));
while (remainingTests.size())
{
int index = rand() % remainingTests.size();
pair<string, TestFuncType> thisTest = remainingTests[index];
remainingTests.erase(remainingTests.begin() + index);
clock_t t0 = clock(); //attempt to preload any caches
(*thisTest.second)(x, 1, -10); //attempt to preload any caches
clock_t t1 = clock();
(*thisTest.second)(x, arg1, arg2);
clock_t t2 = clock();
cout << setw(40) << thisTest.first << " : " << double(t2 -
t1) / double(CLOCKS_PER_SEC) << endl;
}
}
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]