Home Dashboard Directory Help

VC 10 (VS 2010) basic_string exports by njoubert1


 as By Design Help for as By Design

Sign in
to vote
Type: Bug
ID: 696593
Opened: 10/25/2011 7:01:08 AM
Access Restriction: Public
Moderator Decision: Sent to Engineering Team for consideration
User(s) can reproduce this bug


To whom it may concern

The explicit instantiations of the template classes std::basic_string for char and wchar_t, std::allocator for char and wchar_t, string buffer, std::basic_ostringstream etc. seem to be missing. They seem to no longer be exported from the VC 10 runtime libraries. In VC9, these were exported from the runtime libraries and thus allowed custom classes with these types as members to be exported from dlls.

In general, documentation in this regard all points to:

This article (probably out of date) explicitly lists the char and wchar_t instantiations of the template std::basic_string as being exported from the runtime libraries (in addition to others that are missing).

- If this is intentional, why have the exports been removed?
- Please support the exports as in previous versions of the runtimes?

Please find attached VC9 and VC10 samples, the VC9 compiles, the VC10 issues warnings and errors.

We have code based on the exportability of std::string that no longer compiles as required.


Sign in to post a comment.
Posted by njoubert1 on 10/27/2011 at 2:56 AM
Hi Stephan

Thank you for the response. It's comprehensive and quite enlightning. Just as an aside, in general thank you for you feedback on issues, I always find you responses complete and insightful and they have help in understanding several issues in the past.

From the other I've read, I think I understand the "noise" associated with the C4251 warning; but I'd like to confirm based on a sample from our production code. I use the std::string, as well a std::tr1::shared_ptr as private member variables that are not directly exposed to the client, but are only manipulatable via the classes public member functions (there are no protected variables or methods). The shared_ptr is used to implement a pimpl pattern. The class methods are all implemented in the dll (i.e. no inline methods). We don't mix runtimes and compiler options, we have run into issues in the past as well.

My understanding is then that it would be "acceptable" to turn off the warning as in the following sample (relavent detail included)? And that this implementation would allow us to not run afoul of any STL internal data representation problems (reason for the warning)?

class PhysicalAccess {
    // methods

    #pragma warning(push)
    #pragma warning(disable: 4251)
    class Pimpl;
    std::tr1::shared_ptr<Pimpl> impl_;
    #pragma warning(pop)

Please also note: in general we only export abstract classes from dlls (with an appropriate factory), but in this case that wasn't quite the solution we wanted.

Posted by Microsoft on 10/26/2011 at 5:18 PM

Thanks for reporting this issue. I'm resolving it as By Design because this was intentionally changed in VC10. We stopped separately compiling std::string and other machinery like ostringstream into msvcp100.dll, and instead made them header-only like std::vector has always been.

The reason we did this is because std::string's representation is affected by _ITERATOR_DEBUG_LEVEL (previously called _SECURE_SCL and _HAS_ITERATOR_DEBUGGING). There are three possible settings of IDL. 0, the default in release mode, performs zero checking for maximum performance. 1 performs a moderate level of checking for security; if a program misuses the STL, and an attacker is triggering vector overruns (for example), IDL=1 detects this and immediately terminates the program (downgrading whatever the attack would have been, into a denial of service, the least bad result). 2, the default in debug mode, performs exhaustive correctness checking, including iterator invalidation. Compared to IDL=0, both IDL=1 and IDL=2 add extra data members to containers and iterators in order to perform their checking - but this makes the various modes binary-incompatible with each other.

This is problematic because we permit users to modify IDL (the defaults are usually desirable, but not always). VC8/VC9 had only release and debug LIBs and DLLs for the CRT and STL, but there are 5 flavors of IDL (IDL=0/1 for release, IDL=0/1/2 for debug).

In VC10, we build all 5 flavors of static LIBs for the STL. The DLLs are still a problem, though - we have only 2 of them (building 5 isn't feasible because they have to be redistributed). VC8/VC9 attempted to make this work with "magic" in std::string, that tried to make everything work despite potentially different settings of SCL/HID in user code versus the STL's DLL. Unfortunately, this magic never actually worked properly in all cases (there were bugs in all of VC8 RTM, VC8 SP1, VC9 RTM, and VC9 SP1). This caused endless headaches for users trying to modify SCL/HID, so we made std::string header-only in VC10. This eliminated the need for fragile magic. (Additionally, we removed all traces of std::string from the STL DLL's exports, so even though it uses std::string internally, this is not visible.)

The downside of this is what you've encountered - attempting to export a class from a user DLL that derives from std::string or contains it as a data member can trigger warnings and errors. I recommend avoiding this in the first place - putting STL types in your DLL's interface forces you to play by the STL's rules (specifically, you can't mix different major versions of VC, and your IDL settings must match). However, there is a workaround. C4251 is essentially noise and can be silenced, but string is special because of npos, which is affected by an obscure compiler bug. I've previously written an example workaround for a user who was deriving from std::string - your class uses it as a data member but the workaround is the same. Here it is:

C:\Temp>type server.h
#include <string>

    #define MYSTRING_API __declspec(dllexport)
    #define MYSTRING_API __declspec(dllimport)

class MYSTRING_API MyString : public std::string { };

    #ifdef _M_IX86
        #pragma comment(linker, "/include:_magic_magic_magic")
        #pragma comment(linker, "/include:magic_magic_magic")

C:\Temp>type server.cpp
#include "server.h"

C:\Temp>type magic.cpp
#include <string>

template std::string::size_type std::string::npos;
template std::wstring::size_type std::wstring::npos;

extern "C" const int magic_magic_magic = 0;

C:\Temp>type client.cpp
#include <stdio.h>
#include "server.h"

int main() {
    MyString::size_type x = MyString::npos;
    printf("%d\n", static_cast<int>(x));

C:\Temp>cl /EHsc /nologo /W4 /wd4251 /MDd /LD server.cpp
Creating library server.lib and object server.exp

C:\Temp>cl /EHsc /nologo /W4 /wd4251 /MDd /c magic.cpp

C:\Temp>lib /nologo server.lib magic.obj

C:\Temp>cl /EHsc /nologo /W4 /wd4251 /MDd client.cpp server.lib


The important thing about this workaround is that magic.cpp must be completely isolated from all other code - don't include any PCHes there.

If you have any further questions, feel free to E-mail me at stl@microsoft.com .

Stephan T. Lavavej
Visual C++ Libraries Developer
Posted by MS-Moderator10 [Feedback Moderator] on 10/25/2011 at 9:55 PM
Thank you for submitting feedback on Visual Studio 2010 and .NET Framework. Your issue has been routed to the appropriate VS development team for investigation. We will contact you if we require any additional information.
Posted by MS-Moderator01 on 10/25/2011 at 7:48 AM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(http://support.microsoft.com)
Sign in to post a workaround.
File Name Submitted By Submitted On File Size  
exporter_vc10.zip 10/25/2011 5 KB
exporter_vc9.zip 10/25/2011 5 KB