This is the mail archive of the
cygwin
mailing list for the Cygwin project.
cygwin, g++, templates, and DLLs
- From: Charles Wilson <cygwin at cwilson dot fastmail dot fm>
- To: cygwin at cygwin dot com
- Date: Tue, 18 Apr 2006 21:13:18 -0400
- Subject: cygwin, g++, templates, and DLLs
I've been struggling with this problem for over a week now, and I've
read every old (and not so old) thread on cygwin, mingw (and even MS)
mailing list I could find.
(1) you have a DLL whose source code includes a template class.
(2) that template class has a static member variable
(3) the DLL and the DLL's client both use a specific instantiation of
that template.
But the DLL has one copy of the static member var, and the app has a
different copy.
Now, because only Comeau has implemented the 'export' keyword for
templates, you really can't "export" a template from a DLL. From my
reading, it appears that instead, you must export a specific complete
instantiation:
template class __declspec(dllexport|dllimport) MyTemplate< int >;
where the declspec argument is dllexport or dllimport, depending on
whether you're building the DLL or using the DLL. That kinda makes
sense. Except it doesn't always work...
I've uploaded a test case
ftp://cygutils.fruitbat.org/pub/cygutils/TestCase.tar.bz2
which contains the actual source code [*]
352 TestCase/TestDLL/force.cpp
136 TestCase/TestDLL/force.h
271 TestCase/TestDLL/init.cpp
341 TestCase/TestDLL/init.h
1222 TestCase/TestDLL/TestDLL.h
1271 TestCase/TestDLL/TestT.cpp
603 TestCase/TestDLL/TestT.h
335 TestCase/TestExe/main.cpp
and the pre-precessed code with a Makefile that builds the DLL and App.
820739 TestCase/force.ii
820622 TestCase/init.ii
820768 TestCase/main.ii
372 TestCase/Makefile
[*] TestDLL.h #includes 'Support/config.h' which is an entry point into
a whole mess of configury junk that I could not publish. As far as this
test case is concerned, config.h's job is simply to ensure that the
macros in TestDLL.h are properly defined (dllexport, dllimport, etc)
So, since I couldn't include that stuff, the Makefile uses the
pre-processed .ii files directly.
If you compile and link this test, you'll see the following generated
output:
$ ./TestExe
creating new list<T>
ADDING: dll-static
The FULL List:
dll-static
~~~~~~~~~~~~~~~~~~~~~~
The DLL is loaded
ADDING: dll-call-into
The FULL List:
dll-static
dll-call-into
~~~~~~~~~~~~~~~~~~~~~~
ADDING: dll-stack
The FULL List:
dll-static
dll-call-into
dll-stack
~~~~~~~~~~~~~~~~~~~~~~
creating new list<T> <<<<<< [1]
ADDING: APP-stack
The FULL List:
APP-stack <<<<<< [2]
~~~~~~~~~~~~~~~~~~~~~~
ADDING: APP-explicit
The FULL List:
APP-stack <<<<<< [3]
APP-explicit <<<<<< [3]
~~~~~~~~~~~~~~~~~~~~~~
ADDING: dll-call-into
The FULL List:
dll-static
dll-call-into
dll-stack
dll-call-into
~~~~~~~~~~~~~~~~~~~~~~
At [1], a *second* static data member is allocated for this template
specialization. That's bad. At [2], an entry is added (from the app)
into this empty data member -- when it *should* be added into the data
member allocated by the DLL, and which already contains three other
entries, instead. At [3], same song, second verse. However, if the app
calls a function in the DLL, which THEN calls a static method on the
template specialization, the "correct" static member var -- the one in
the DLL -- is modified. So if the app call that template class's static
method directly the "wrong" member var is modified -- but the app can
call "indirectly" thru a different function exposed by the DLL.
Inspecting the .ii files, we see that in init.ii we have
template class __attribute__((dllexport)) TESTDLL::TestT< std::string >;
and in force.ii we ALSO have
template class __attribute__((dllexport)) TESTDLL::TestT< std::string >;
Looking in the .o's for the static data member ('theList') using 'nm -a
--demangle force.o | grep theList', we get:
force.o:
00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE
00000000 D TESTDLL::TestT<std::string>::theList
init.o:
00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE
00000000 D TESTDLL::TestT<std::string>::theList
This is okay, because the ld knows how to merge these duplicates when
linking the DLL, as we can see by using objdump -t on the DLL. That
shows only one copy in the symbol table:
[ 16](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000140
.data$_ZN7TESTDLL5TestTISsE7theListE
[5239](sec 2)(fl 0x00)(ty 0)(scl 2) (nx 0) 0x00000140
TESTDLL::TestT<std::string>::theList
This is all exactly as I'd expect. So now, let's look at the app:
in the main.ii file, I see
template class __attribute__((dllimport)) TESTDLL::TestT< std::string >;
which is supposedly The Right Thing To Do.
however, using nm on main.o, I get
00000000 d .data$_ZN7TESTDLL5TestTISsE7theListE
00000000 D TESTDLL::TestT<std::string>::theList
which is exactly what appears in the DLL .o's -- and that's bad.
Can anybody tell me what's really going on? I've two (feared)
possibilities:
2005-08-10:
https://sourceforge.net/tracker/?func=detail&atid=102435&aid=1255376&group_id=2435
where Danny says "A patch I recently submitted to GCC
fixes the bug on trunk (4.1). The patch is undergoing revison
to make it acceptable." but there is no indication it actually made it
in to 4.1...
OR the "mystery bug" danny mentioned at that same link ("is due to YA
dllimport bug (that one was about template instantiations erroneously
being marked as dllimport)" but which I can find no other mention, nor a
link to the gcc-patches list. (Of course, it seems MY problem is that a
template instantiation, explicitly declared dllimport, is NOT getting
marked properly).
Even if one of these two issues is the problem -- and has been fixed --
it means I must build a 4.1 compiler...is that true, or <wild hope> is
there are very simple fix that I'm just not seeing? </wild hope>
So, Assorted Smart People Who Know About Cygwin/Mingw-GCC Internals, any
advice?
--
Chuck
--
Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple
Problem reports: http://cygwin.com/problems.html
Documentation: http://cygwin.com/docs.html
FAQ: http://cygwin.com/faq/