Compile error for (char based) STL (stream) containers in Visual Studio

后端 未结 2 1002

This is basically the same question as [SO]: C2491: \'std::numpunct<_Elem>::id\' : definition of dllimport static data member not allowed [closed], but considering the follow

2条回答
  •  滥情空心
    2021-01-21 10:03

    Start notes:

    • I am using VStudio Community 2015 (v14.0.25431.01 Update 3). Version is important here, since standard header files might change across versions (and line numbers might differ)
    • Created [MSDN]: Compile error for STL (stream) containers in Visual Studio

    Approaches:

    1. Quick (shallow) investigation

      On VStudio IDE double click, on the 2nd note in the Output window (after attempting to compile the file), and from there repeated RClicks on relevant macros, and from the context menu choosing Go To Definition (F12):

      • xlocnum (#120): (comment is part of the original file/line)

        __PURE_APPDOMAIN_GLOBAL _CRTIMP2_PURE static locale::id id; // unique facet id
        
      • yvals.h: (#494):

             #define _CRTIMP2_PURE _CRTIMP2
        
      • crtdefs.h (#29+):

        #ifndef _CRTIMP2
            #if defined CRTDLL2 && defined _CRTBLD
                #define _CRTIMP2 __declspec(dllexport)
            #else
                #if defined _DLL && !defined _STATIC_CPPLIB
                    #define _CRTIMP2 __declspec(dllimport)  // @TODO - cfati: line #34: Here is the definition
                #else
                    #define _CRTIMP2
                #endif
            #endif
        #endif
        

      As seen, __declspec(dllimport) is defined on line #34. Repeating the process on the _DLL macro, yielded no result. Found on [MSDN]: Predefined Macros:

      _DLL Defined as 1 when the /MD or /MDd (Multithreaded DLL) compiler option is set. Otherwise, undefined.

      I thought of 2 possible ways to go on (both resulting in a successful build):

      • Use static version of CRT Runtime ([MSDN]: /MD, /MT, /LD (Use Run-Time Library)). I don't consider it a viable option, especially when the project consists of .dlls (and it does): bad things can happen (e.g. [SO]: Errors when linking to protobuf 3 on MSVC 2013, or even nastier ones can occur at runtime)
      • Manually #undef _DLL (in main.cpp, before any #include). This is a lame workaround (gainarie). It builds fine, but tampering with these things could (and most likely will) trigger Undefined Behavior at runtime

      None of these 2 options was fully satisfactory, so:

    2. Going a (little) bit deeper

      Tried to simplify things even more (main.cpp):

      #include 
      
      
      //typedef unsigned short CharType;  // wchar_t  unsigned short
      #define CharType unsigned short
      
      
      int main() {
          std::basic_stringstream stream;
          CharType c = 0x41;
          stream << c;
          return 0;
      }
      

      Notes:

      • Replaced typedef by #define (to strip out new type definition complexity)
      • Switched to unsigned short which is wchar_t's definition (/Zc:wchar_t-) to avoid any possible type size / alignment differences


      "Compiled" the above code with [MSDN]: /E (Preprocess to stdout) and [MSDN]: /EP (Preprocess to stdout Without #line Directives) (so that the warnings/errors only reference line numbers from current file):

      • Generated preprocessed files (using each flag froma bove): ~1MB+ (~56.5k lines)
      • The only difference in the files was the #define (wchar_t vs. unsigned short) somewhere at the very end
      • Compiling the files (shockingly :)) yielded the same result: the wchar_t one compiled while the unsigned short failed with the same error
      • Added some #pragma message statements (yes, they are handled by the preprocessor, but still) in the file that fails (before each warning/note), noticed some difference between the 2 #defines, but so far unable to figure out why 1
      • While browsing the generated file(s), noticed a template<> struct char_traits definition, so I gave it a try, and it worked (at least the current program compiled) 1 (and, as expected sizeof(char32_t) is 4). Then, found [MSDN]: char, wchar_t, char16_t, char32_t


      Notes:

      • Although this fixed my current problem (still don't know why), will have to give it a shot on the end goal
      • 1 Although I looked over the file, I didn't see any template definitions targeting only the "privileged" types (e.g. I didn't see anything that would differentiate wchar_t, signed char or char32_t from unsigned short for example), so I don't know (yet) why it works for some types but not for others. This is an open topic, whenever I'll get new updates, I will share them

    Bottom line:

    As empirically discovered, the following types are allowed, when working with char based STL containers:

    • char
    • unsigned char
    • signed char
    • wchar_t
    • char16_t
    • char32_t
    • unsigned short (/Zc:wchar_t- only )

    Final note(s):

    • I will incorporate anything useful (e.g. comments) in the answer

    @EDIT0:

    • Based on @IgorTandetnik's answer on [MSDN]: Compile error for STL (stream) containers in Visual Studio, although there is still a little bit of fog left on:

      • unsigned char and signed char
      • Difference between static and dynamic C++ RTLib


      I'm going to accept this as an answer.

提交回复
热议问题