Using STL without exceptions in Visual Studio 2005

Cover of the C++ STL book

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.

Simple code using STL

Now you go to your project settings and turn off exceptions as below.

Project Setting 1

Project Settings 2

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值