There are so many things in life that are so simple that they should be well documented in several places and therefore accessible or easy to search for everyone with a simple query on Google.
Using STL with exceptions turned off in VS2005 is one of them. Unfortunately it seems that most pages are questions around the same issues without any clear answer, or even worse pages with comments with questions about the reasons for not using exceptions or arguments for not doing so.
If you arrived on this page, and simply are looking for a solution to your problem, you found the right place! You must have your personal reasons for doing so, and I trust you know what you are doing!
- Short solution
If you use the DLL version of the Runtime libraries, add the following two lines of code before including any STL code, and disable exceptions in your project settings.
#define _HAS_EXCEPTIONS 0
#define _STATIC_CPPLIB
If you link with the static version of the Runtime libraries, there is no quick and satisfying robust option. There are quick and dirty ways that I do not recommend though, but I provide anyway at the end of this entry.
- Long solution
Imagine you have written a program using STL as the one below.
Now you go to your project settings and turn off exceptions as below.
As always things are not as easy as they should and you get welcomed by the following warning message:
1>c:/program files/microsoft visual studio 8/vc/include/vector(1141) : warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
You might think, it is just a warning and it is going to be fine, but if you wanted to disable exceptions to reduce your code size, this is not solving much. Great! So you double click on the warning and fall into STL code. This is usually discouraging for most, but is actually very instructive as always. I always found that looking into include files was a good way to learn a lot about the code Few lines above there is an interesting define being used:
#if _HAS_ITERATOR_DEBUGGING
Ok, this is interesting. Let’s see where this is defined and check what are other related defines. A single right click with Visual Assist (Which I vividly recommend to every programmer by the way) brings us to yvals.h
And scrolling up few lines shows something very interesting.
#ifndef _HAS_EXCEPTIONS
#define _HAS_EXCEPTIONS 1 /* predefine as 0 to disable exceptions */
#endif /* _HAS_EXCEPTIONS */
Funnily, it also shows that _HAS_NAMESPACE seems to have been victim of copy-paste syndrom since it shows like that:
#ifndef _HAS_NAMESPACE
#define _HAS_NAMESPACE 1 /* predefine as 0 to disable exceptions */
#endif /* _HAS_NAMESPACE */
Anyways, we found a great candidate to solve our problem. So we add a line of code with #define _HAS_EXCEPTIONS 0 and compile. What a surprise, it is not working! Now we are greeted again with link errors:
1>Main.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) public: void __thiscall std::exception::_Raise(void)const ” (__imp_?_Raise@exception@std@@QBEXXZ) referenced in function “protected: static void __cdecl std::vector >::_Xlen(void)” (?_Xlen@?$vector@HV?$allocator@H@std@@@std@@KAXXZ)
1>Main.obj : error LNK2019: unresolved external symbol “__declspec(dllimport) public: __thiscall std::exception::exception(char const *,int)” (__imp_??0exception@std@@QAE@PBDH@Z) referenced in function “public: __thiscall std::logic_error::logic_error(class std::basic_string,class std::allocator > const &)” (??0logic_error@std@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@@Z)
What is interesting in the linker error is the __declspec(dllimport) part. We are not compiling a DLL, so there is no reason why this should be flagged as an import. Looking at _Xlen() shows that it calls the _Raise() method of the length_error class which is derived from logic_error itself derived from exception. So we open the include file.
The two methods are found in the std::exception class and defined even if _HAS_EXCEPTIONS is 0. Things become interesting. _Raise() is prefixed by __CLR_OR_THIS_CALL and exception by _CRTIMP2.
Visual Assist shows _CRTIMP2 being defined in several include files: yvals.h, wctype.h, crtdefs.h and new.h! Adding #error and compiling four times shows that only yvals.h and crtdefs.h are currently being used. And it also shows that _CRTIMP2 is defined in crtdefs.h in the following code:
#ifndef _CRTIMP2
#if defined(_DLL) && !defined(_STATIC_CPPLIB)
#define _CRTIMP2 __declspec(dllimport)
#else /* ndef _DLL && !STATIC_CPPLIB */
#define _CRTIMP2
#endif /* _DLL && !STATIC_CPPLIB */
#endif /* _CRTIMP2 */
And of course _DLL is defined without a valid reason in my case since I am compiling a command line program, and _STATIC_CPPLIB is not defined.
So we have a solution. Adding the following two lines of code before including any STL code fixes the issue:
#define _HAS_EXCEPTIONS 0
#define _STATIC_CPPLIB
Still that does not explain why _DLL is defined. Searching in files is no _DLL defined in code that is actually compiled with our code, but looking at the compile options shows that it is implicitly defined when using a DLL as Runtime library in the Code Generation options of the project settings. Changing to static libraries brings other link errors.
1>LIBCMTD.lib(stdexcpt.obj) : error LNK2005: “public: virtual __thiscall std::exception::~exception(void)” (??1exception@std@@UAE@XZ) already defined in Main.obj
1>LIBCMTD.lib(stdexcpt.obj) : error LNK2005: “public: virtual char const * __thiscall std::exception::what(void)const ” (?what@exception@std@@UBEPBDXZ) already defined in Main.obj
And of course there are plenty of reasons why you might still want to link LIBCMTD.lib, excluding might not be an option for you. So the problem is not only that those functions are defined twice but that LIBCMTD.lib has been compiled with exceptions enabled. There are three options.
The first one and best option is to recompile libc with exceptions disabled. There is a good source here. I might write another blog entry about that in the future.
The second one is to make sure exceptions are not creating duplication. Adding the following code will quickly make the work, and might seem to be a good idea but it is not, since it is just hiding the issues and not solving them.
#define _HAS_EXCEPTIONS 0
#define _EXCEPTION_
namespace std
{
class exception
{
public:
void _Raise() const {};
};void _Throw( const exception& except );
class bad_alloc : public exception
{
public:
bad_alloc( const char *_Message ) {};
};
}
The last one which might look like a great trick (but is not) is simply to tell the linker to ignore the second definition of the functions that come from LIBCMTD.lib with the linker option /FORCE. Again it is a bad idea since it ignores one of the two implementations. The first one exception::what is not a big issue but the destructor exception::~exception will be. Although it might work in your tests this might bring havoc at some point.