This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]