6

I am trying to do some baic char16_t string (u16string) processing, and have ran into some troubles. This short program:

#include <string>
#include <sstream>

int main()
{
    int foo = 65;

    std::basic_stringstream<char16_t> ss;
    ss << foo;

    std::u16string s = ss.str();
}

Creates the error:

Error   C2491   'std::numpunct<_Elem>::id': definition of dllimport static data member not allowed. xlocnum 259

I have tried this on some online compilers, but there is no error there.

Thanks for any help I can get!

Russell Greene
  • 2,141
  • 17
  • 29
  • Looks like a bug to me. Probably something related to the way Visual C++ ships with pre-built versions of certain standard types in its runtime library. It's not a fix per se, but if you link against the static runtime library instead of the DLL runtime library, the code seems to compile. – DrPizza Aug 14 '15 at 06:28

1 Answers1

4

OK, it looks like a bug in the VC++ standard libraries or in the VC++ compiler, or perhaps even both.

<xlocnum>, line 85, declares within class numpunct:

__PURE_APPDOMAIN_GLOBAL _CRTIMP2_PURE static locale::id id; // unique facet id

<xlocnum>, line 258/259 defines:

template<class _Elem>
    __PURE_APPDOMAIN_GLOBAL locale::id numpunct<_Elem>::id;

_CRTIMP2_PURE is defined as _CRTIMP2, which in turn is defined as __declspec(dllimport).

Now, according to my reading of the VC++ documents, that should be OK. __declspec(dllimport) is permitted on static declarations. It isn't, however, permitted on static definitions. But the definition doesn't have the __declspec(dllimport), only the declaration does.

Nonetheless, an error is being produced: the compiler is seeing the definition, treating it as if it were __declspec(dllimport), and producing an error.

The reason I'm not sure if it's a compiler error or a library error is that the compiler also emits a warning to complain that the declaration and definition don't match--that one is __declspec(dllimport) and the other is not. Since the definition cannot, per the documentation, be __declspec(dllimport), that suggests to me that neither the declaration nor the definition should be __declspec(dllimport).

If we look at other similar members, this suspicion is confirmed. For example, num_get::id is not _CRTIMP2_PURE, nor is num_put::id.

So there are I think two possibilities. One is that the _CRTIMP2_PURE is in error, and that it should be removed. The other is that the compiler is issuing a faulty diagnostic when it claims that the definition is __declspec(dllimport), when it isn't.

Either way, I think that the code sample should compile and this is something that Microsoft will need to fix.

DrPizza
  • 17,882
  • 7
  • 41
  • 53
  • 1
    A good analysis. Comments: The compiler doesn't say that the definition is `dllimport`; it says that you're not allowed to define a data member that was previously declared `dllimport`. This is correct: `dllimport` means "I'll find the definition in a DLL, where it was previously (separately) compiled using `dllexport`". Providing a definition for something declared `dllimport` in a translation unit doesn't make sense, and the diagnostic is correct. – bogdan Aug 14 '15 at 13:28
  • The reason it works for `char` and `wchar_t` is that `xlocnum` contains explicit instantiations for these arguments (at the end of the file). They look strange, as they use the syntax for explicit instantiation *definitions* (they should be `extern template`, not just `template`), but it looks like MSVC considers them as just declarations if there's a previous `dllimport` declaration (this is just a guess, otherwise they wouldn't work either). Anyway, I agree with your conclusion that this needs to be fixed by MS; more precisely, in MSVC's standard library. This should be reported. – bogdan Aug 14 '15 at 13:39
  • Oh yes, while it's OK to define functions that are `dllimport`, because `dllimport` is advisory (it's a performance optimization used to inline thunks), I'd forgotten that data definitions were different. It's hard to tell for sure, but it looks like this is actually an old bug: http://stackoverflow.com/questions/8401359/c2491-stdnumpunct-elemid-definition-of-dllimport-static-data-member-n The questioner doesn't provide much detail, though. – DrPizza Aug 14 '15 at 13:42
  • Is the code you're referencing from the VC + + 14 compiler? – Russell Greene Aug 14 '15 at 15:11
  • Do you think that this is the same bug? https://connect.microsoft.com/VisualStudio/Feedback/Details/1348277 – Russell Greene Aug 14 '15 at 16:14
  • @RussellGreene That one is a bit different: `std::codecvt` is explicitly specialized for those arguments (inside `xlocale`), the explicit specialization is specified as `dllimport`, and there is no attempt to define the static `id` member in the header, which is correct. However, that definition should be exported from the standard library DLL, and, apparently, it is not, hence the linker error. In short, your problem is in the header mentioned above, this one is in the DLL. – bogdan Aug 14 '15 at 17:15
  • Yea, I commented out the `CRTIMP2_PURE` and it works perfectly. I will try to file a bug report. – Russell Greene Aug 14 '15 at 17:18