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

3

5

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 following facts:

  • That (on my opinion) is a perfectly valid question (according to [SO]: How to create a Minimal, Complete, and Verifiable example), I really don't know why some users felt the urge of closing it
  • The answer marked as a solution provides guidelines the for fixing the error (in general), but doesn't apply to current case, and certainly, doesn't fix it,

please don't close it or mark it as a duplicate (at least, not without carefully reading and understanding it).

main.cpp:

#include <sstream>


//#define THROW_C2491
#if defined(THROW_C2491)
typedef int CharType;
#else
typedef char CharType;
#endif


int main() {
    std::basic_stringstream<CharType> stream;
    CharType c = 0x41;
    stream << c;
    return 0;
}

The code is slightly modified (simplified), and fails to compile if THROW_C2491 is defined:

xlocnum(294): error C2491: 'std::numpunct<_Elem>::id': definition of dllimport static data member not allowed

Output:

E:\Work\Dev\StackOverflow\q048716223>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" amd64

E:\Work\Dev\StackOverflow\q048716223>
E:\Work\Dev\StackOverflow\q048716223>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\bin\amd64\cl.exe" /GS /W3 /Zc:wchar_t /ZI /Gm /Od /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /EHsc /nologo -c "src\main.cpp"
main.cpp

E:\Work\Dev\StackOverflow\q048716223>echo %errorlevel%
0

E:\Work\Dev\StackOverflow\q048716223>
E:\Work\Dev\StackOverflow\q048716223>"c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\bin\amd64\cl.exe" /GS /W3 /Zc:wchar_t /ZI /Gm /Od /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /EHsc /nologo -c "src\main.cpp" /D "THROW_C2491"
main.cpp
c:\install\x86\microsoft\visual studio community\2015\vc\include\xlocnum(294): warning C4273: 'id': inconsistent dll linkage
c:\install\x86\microsoft\visual studio community\2015\vc\include\xlocnum(120): note: see previous definition of 'public: static std::locale::id std::numpunct<int>::id'
c:\install\x86\microsoft\visual studio community\2015\vc\include\xlocnum(120): note: while compiling class template static data member 'std::locale::id std::numpunct<_Elem>::id'
        with
        [
            _Elem=CharType
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\xlocnum(1261): note: see reference to function template instantiation 'const _Facet &std::use_facet<std::numpunct<_Elem>>(const std::locale &)' being compiled
        with
        [
            _Facet=std::numpunct<CharType>,
            _Elem=CharType
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\xlocnum(1255): note: while compiling class template member function 'std::ostreambuf_iterator<_Elem,_Traits> std::num_put<_Elem,std::ostreambuf_iterator<_Elem,_Traits>>::do_put(_OutIt,std::ios_base &,_Elem,bool) const'
        with
        [
            _Elem=CharType,
            _Traits=std::char_traits<CharType>,
            _OutIt=std::ostreambuf_iterator<CharType,std::char_traits<CharType>>
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\ostream(305): note: see reference to class template instantiation 'std::num_put<_Elem,std::ostreambuf_iterator<_Elem,_Traits>>' being compiled
        with
        [
            _Elem=CharType,
            _Traits=std::char_traits<CharType>
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\ostream(291): note: while compiling class template member function 'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(int)'
        with
        [
            _Elem=CharType,
            _Traits=std::char_traits<CharType>
        ]
e:\work\dev\stackoverflow\q048716223\src\main.cpp(16): note: see reference to function template instantiation 'std::basic_ostream<_Elem,_Traits> &std::basic_ostream<_Elem,_Traits>::operator <<(int)' being compiled
        with
        [
            _Elem=CharType,
            _Traits=std::char_traits<CharType>
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\istream(939): note: see reference to class template instantiation 'std::basic_ostream<_Elem,_Traits>' being compiled
        with
        [
            _Elem=CharType,
            _Traits=std::char_traits<CharType>
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\sstream(574): note: see reference to class template instantiation 'std::basic_iostream<_Elem,_Traits>' being compiled
        with
        [
            _Elem=CharType,
            _Traits=std::char_traits<CharType>
        ]
e:\work\dev\stackoverflow\q048716223\src\main.cpp(14): note: see reference to class template instantiation 'std::basic_stringstream<CharType,std::char_traits<_Elem>,std::allocator<_Elem>>' being compiled
        with
        [
            _Elem=CharType
        ]
c:\install\x86\microsoft\visual studio community\2015\vc\include\xlocnum(294): error C2491: 'std::numpunct<_Elem>::id': definition of dllimport static data member not allowed
        with
        [
            _Elem=CharType
        ]

E:\Work\Dev\StackOverflow\q048716223>echo %errorlevel%
2

Notes:

  • Everything is VStudio 2015 specific, but the problem is reproducible using VStudio 2017, VStudio 2013, VStudio 2010 (of course, the line numbers differ). However, it works using VStudio 2005
  • I chose to paste the compiler commands(s) and output(s) from the cmdline, instead of placing pictures with compiler flags and output (from VStudio IDE), so it's easier to reproduce (if anyone is willing to try)
  • The compiler flags are defaults from an Application (.exe) VStudio project (Win32), except some irrelevant path related ones (e.g. .pch file and other such crap), that I stripped out
  • Compiles only if CharType is char (or any of the narrow character types, as a matter of fact) or wchar_t. Disabling [MS.Docs]: /Zc:wchar_t (wchar_t Is Native Type) adds unsigned short to the list (which seems natural)
  • Compiles without problems under Lnx (Ubtu 16 x64) / g++ (gcc 5.4.0)

[MS.Docs]: Compiler Error C2491 is pretty straightforward, I am familiar with it, there are some answers (e.g. [SO]: Linker error when calling a C function from C++ code in different VS2010 project (@CristiFati's answer), [SO]: Excel VBA, Can't Find DLL Entry Point from a DLL file (@CristiFati's answer)) to back me up.

Considering the above notes, I know that it's something that has to do with Win's way (limitation?) of handling chars, but I don't see any (immediate) connection between the code and the error. Nothing regarding this behavior on [MS.Docs]: basic_stringstream Class. I (shallowly) browsed the involved standard include headers, but I didn't get to the bottom of it so far. Am I missing smth extremely obvious?

Other references (same or similar error, but none containing a valid fix):


It is worth mentioning that the end goal is to build some 3rd-party software that instantiates some 32 bit char based streams.

Stria answered 10/2, 2018 at 1:8 Comment(7)
think valid CharType only char, unsigned short and wchar_t. for all another types will be this errorRuella
@RbMm: I posted an answer, the good chars are at the end. I launched the question, already have some answers, but not to the point that I'm (intelectually) satisfied with them.Stria
What's your problem? MSVC only allow some specific types for CharType, this is a fact. Are you seeking for how MSVC forbids other types, or why MSVC forbids other types, or what types are allowed?Segalman
@xskxzr: "MSVC only allow some specific types for CharType, this is a fac" Could you elaborate? Regarding the statement "this is a fact", I totally disagree. And yes, my problem (at its rootcause) is failing to understand failure...Stria
Because you cannot compile with other types... isn't this a fact?Segalman
I'm sorry I'm not going into a polemica...Stria
DownVoter, could you please explain yourself?Stria
S
4

Start notes:

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 <sstream>
    
    
    //typedef unsigned short CharType;  // wchar_t  unsigned short
    #define CharType unsigned short
    
    
    int main() {
        std::basic_stringstream<CharType> 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<char32_t> 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:

Stria answered 10/2, 2018 at 1:56 Comment(0)
U
0

The problem occurs because in the templated class std::numpunct, the public variable id is declared with _CRTIMP2_PURE_IMPORT. I haven't chased down the definition of this, but it presumably sets this variable to be imported from the C runtime dll.

The way to fix the problem is to specialize the std::numpunct class for the character type you wish to use, but declare the id variable without _CRTIMP2_PURE_IMPORT.

As the OP refers to a now out of date version of Visual Studio, I won't try and reference specific files or line numbers, as they may change from version to version. I also won't provide a specific specialization that can be used, as this probably depends upon exactly what the OP wants (it may be easy if they wish to just use their locale, or a bit more difficult if they wish to use other/any locale).

Unorganized answered 13/12, 2020 at 21:15 Comment(0)
C
0

Not an answer, but a note to say things are no better three years later. Here's what I get in VS2019 v16.9.4

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29910\include\xlocnum(263,1): error C2491: 'std::numpunct<_Elem>::id': definition of dllimport static data member not allowed

trying to use uint64_t as the character type (Writing a NCurses C++ library, and my version of PDCurses uses 64 bit chtypes...

Caplan answered 3/5, 2021 at 12:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.