• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

C++ i DLL - nie mogę używać obiektów typów złożonych, warning dll-interface

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
0 głosów
627 wizyt
pytanie zadane 27 lutego 2016 w C i C++ przez JGP Początkujący (270 p.)

Pracuję właśnie nad biblioteką do obsługi pewnego rodzaju mojego pliku. Wcześniej tą funkcjonalność miałem zintegrowaną w trzech klasach z kodem gry, ale zdecydowałem się na wyodrębnienie tego kawałka kodu do DLL'ki.

Zauważyłem, że jeśli w klasach używam typów prostych, jak int czy char*, wszystko jest w porządku. Dotychczas w moim managerze wykorzystywałem obiekty vector i string - vector który zawiera stringi. Gdy tego samego wektora ze stringami używam w DLL, kompilator wypisuje mi warning C4251 o treści:

class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'test'

tak samo jest z 'std::vector<_Ty>'.

Moje pytanie więc. Jak zdefiniować wspomniany "dll-interface" aby móc swobodnie korzystać z klasy vector, string czy dowolnej, innej w bibliotece DLL?

Obecnie nie mogę używać stringów ponieważ program się wykrzacza. Gdy eksperymentalnie zmieniłem stringi na łańcuchy char*, wszystko jest jak należy. Dlatego podejrzewam, że problem ma związek z tym warunkiem o "dll-interface"

Czy może mieć to jakiś związek ze stosem? Zanim doszedłem do tego co teraz powyżej opisałem miałem różne przypadki i błędy i czytałem m.in. o konflikcie stosu aplikacji i biblioteki, czy coś w ten deseń.

Dodam, że bibliotekę linkuję statycznie.

1 odpowiedź

0 głosów
odpowiedź 27 lutego 2016 przez criss Mędrzec (172,590 p.)
wybrane 1 marca 2016 przez JGP
 
Najlepsza

Zainteresowałem się, bo możliwe, że niedługo będę miał podobny problem. Znalazłem to: http://stackoverflow.com/questions/8976617/when-exporting-stl-stdbasic-string-template-from-dll-i-get-a-lnk2005-error

Możliwe, że będziesz zmuszony do używania const char* zamiast std::string.

Druga odpowiedź wyjaśnia, co się dzieje:

Link to MS article that you presented says that some STL classes "...are already exported by the C Runtime DLL. Therefore, you cannot export them from your DLL. ". Including basic_string. And your link error says that basic_string symbol "...already defined in OtherClass.obj". Because linker sees two equal symbols in two different places.

komentarz 29 lutego 2016 przez JGP Początkujący (270 p.)

Ze stringami zrobiłem tak, że w dll'ce korzystam z nich, ale funkcje zwracają je jako const char*, więc ta kwestia odchodzi i kod niby działa poprawnie bo otrzymuję prawidłowe łańcuchy znaków.
Jednak warning C4251 otrzymuję nadal. Zarówno przez klasę string jak i przez wektor, który zawiera obiekty reprezentujące mój fizyczny plik.

Znalazłem taki artykuł: https://support.microsoft.com/en-us/kb/168958 . Wydaje się być tym o co chodzi. Ustawiłem opcje tak jak jest podane dla przykładu dll.cpp. To są: /GX /LDd /MDd /D"EXP_STL". Lecz z tą pierwszą coś nie do końca jest w porządku:

1>cl : Command line warning D9035: option 'GX' has been deprecated and will be removed in a future release
1>cl : Command line warning D9036: use 'EHsc' instead of 'GX'

Poza tym, wcześniej nie uwzględniałem importu w projekcie aplikacji i daną klasę tylko eksportowałem - definiowałem ją jako "class __declspec(dllexport) 'nazwa'". Z omawianymi bibliotekami mam styczność od kilku dni a skoro tak działało to tak robiłem.
Dodałem więc definicję preprocesora w projekcie DLL tak jak to jest w przykładzie MYHEADER.H.
Myślałem, że te zmiany dadzą w pełni poprawnie kompilowany projekt, jednak to jeszcze nie wszystko. Na tym etapie nadal otrzymuję warning C4251 przez obiekty string i vector o którym pisałem powyżej.

Doszedłeś może do czegoś co trzeba by jeszcze zmienić?
W moim głównym pliku nagłówkowym dopisałem jeszcze instrukcje: EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<LNGfile*>; . Lecz otrzymuję błąd:

error C2946: explicit instantiation; 'LNGfile' is not a template-class specialization

Obiekty tej klasy właśnie przechowuję w wektorze i pewnie ją (jej definicję...) trzeba by jeszcze nieco zmodyfikować.

Ciekawie wygląda przykład na końcu artykułu "EXE.CPP". W nim, na końcu main znajduje się vector<MyClass> i obiekty tej klasy są poprawnie do niego wkładane.

komentarz 29 lutego 2016 przez criss Mędrzec (172,590 p.)

Nie czytałem o tym nic więcej póki co, w niedalekiej przyszłości będę musiał przez to przejść, ale jeszcze nie teraz :P Poza tym ja korzystam z GCC, więc u mnie to pewnie będzie wyglądać inaczej (te opcje są specyficzne dla MSVC z tego co widze).

Co do tych stringów... czyli w ciele funkcji normalnie używasz stringów, ale typem zwracanym musi być const char*? Tutaj cały czas mówisz o funkcjach w dll?

1>cl : Command line warning D9035: option 'GX' has been deprecated and will be removed in a future release 
1>cl : Command line warning D9036: use 'EHsc' instead of 'GX'

Kompilator ci podpowiada: używaj EH (z argumentami s i c - tutaj nie wiem dlaczego akurat te) zamiast GX, bo GX jest przestarzałe.

Co do tej deklaracji: w taki sposób deklarujesz specjalizacje template-a, tak chyba nie można (chociaż w przykładzie tak jest... nie ogarniam), choć nie jestem pewien dlaczego. Spróbuj tak:

EXPIMP_TEMPLATE template<class T> class DECLSPECIFIER std::vector;
//

Nie wiem czy pomoże, bo nawet nie wiem czemu ta deklaracja ma służyć. Anyway - dzięki za podzielenie się swoim rozwiązaniem, pewnie zaoszczędzi mi dużo problemów :D

komentarz 1 marca 2016 przez JGP Początkujący (270 p.)
edycja 1 marca 2016 przez JGP

Ok, w końcu mam! Szukałem tak po omacku ponad półtorej godziny, aż w końcu natrafiłem na artykuł, który mnie oświecił: http://stackoverflow.com/questions/16419318/one-way-of-eliminating-c4251-warning-when-using-stl-classes-in-the-dll-interface

Chodzi o to, że nie potrzebnie eksportowałem albo importowałem całą klasę, czyli:

#ifdef MYHEADER_H
#    define DECLSPECIFIER __declspec(dllexport)
#else
#    define DECLSPECIFIER __declspec(dllimport)
#endif

a potem definicja klasy wyglądała tak:

class DECLSPECIFIER nazwa_klasy

Wystarczy eksportować/importować funkcje. Definicja klasy to po prostu "class nazwa_klasy", a dyrektywę DECLSPECIFIER, która zawiera albo __declspec(dllexport), albo __declspec(dllimport) dopisujemy przed wszystkimi publicznymi funkcjami wraz z konstruktorem i destruktorem.

W projekcie DLL'ki w configuration properties/ C/C++ / Preprocessor mam wpisaną dyrektywę MYHEADER_H, dzięki temu jest zdefiniowana i wykona się blok #ifdef - uzyskam eksport tych funkcji, a w projekcie aplikacja tej dyrektywy nie mam wpisanej więc wykona się blok #else.

Miałem jeszcze dyrektywę EXPIMP_TEMPLATE, która w przypadku dllimport była zamieniona na extern, ale ją usunąłem i bez niej wydaje się być wszystko w porządku - zero warunków i błędów w projekcie dll jak i aplikacji.

Co do tych stringów... czyli w ciele funkcji normalnie używasz stringów, ale typem zwracanym musi być const char*? Tutaj cały czas mówisz o funkcjach w dll?

Tak, dokładnie. We wszystkich funkcjach, we wszystkich trzech klasach projektu dll'ki korzystam ze stringów, tylko potem zwracam je jako const char*.

te opcje są specyficzne dla MSVC z tego co widze

Tak, korzystam z Visual'a.

Kompilator ci podpowiada: używaj EH (z argumentami s i c - tutaj nie wiem dlaczego akurat te) zamiast GX, bo GX jest przestarzałe.

Ok, tak zrobiłem, dzięki ;)

Cieszę się, że mogłem pomóc i dzięki również za Twoje wypowiedzi. Bez nich bym pewnie się z tym nie uporał, a na pewno nie dzisiaj ;)

Nie raz już tak właśnie miałem. Choć nie powiedziałeś mi wprost jak to załatwić to sam doszedłem próbując odpowiedzieć na Twoje pytania, więc jak widać warto rozmawiać. Nawet jeśli żaden z rozmówców nie wie do końca o co chodzi :D

komentarz 1 marca 2016 przez criss Mędrzec (172,590 p.)
Super :D Ciesze się, że jakoś pomogłem, chociaż też po omacku. Teraz całe potrzebne info jest w jednym miejscu i tutaj dla Ciebie wielkie dzięki ode mnie i przyszłych gości tego tematu, że chciało ci się wyczerpująco opisywać swoje kroki :)
komentarz 3 marca 2016 przez JGP Początkujący (270 p.)
Dzięki, doceniam ;) Po prostu miło jest nie raz szukając czegoś wpisać to w google i po dwóch minutach trafić na konkretną odpowiedź (tym bardziej w języku polskim ;D ). Dlatego uważam, że każdy w miarę możliwości powinien dać coś od siebie ;)

Jeszcze co zauważyłem to, że przynajmniej Visual 2010 w którym koduję, w projektach DLL sam dodaje dyrektywę preprocesora, o której pisałem, aby sobie utworzyć. Dopisuje ją na końcu dyrektyw jako "nazwa projektu_EXPORTS". Można ją wykorzystać choć oczywiście każdy może mieć własną konwencję nazewnictwa.

Podobne pytania

0 głosów
1 odpowiedź 226 wizyt
pytanie zadane 16 października 2023 w C# przez kubekszklany Gaduła (3,250 p.)
+1 głos
1 odpowiedź 907 wizyt
pytanie zadane 24 sierpnia 2016 w C i C++ przez MrRozgunek Użytkownik (810 p.)
0 głosów
0 odpowiedzi 529 wizyt
pytanie zadane 5 listopada 2020 w C i C++ przez disaster Bywalec (2,120 p.)

93,173 zapytań

142,184 odpowiedzi

321,967 komentarzy

62,502 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 1157p. - dia-Chann
  2. 1139p. - Łukasz Piwowar
  3. 1131p. - CC PL
  4. 1126p. - Łukasz Eckert
  5. 1118p. - Tomasz Bielak
  6. 1104p. - Michal Drewniak
  7. 1083p. - Marcin Putra
  8. 1078p. - rucin93
  9. 1071p. - rafalszastok
  10. 1054p. - Adrian Wieprzkowicz
  11. 1047p. - Piotr Aleksandrowicz
  12. 1037p. - Michał Telesz
  13. 1005p. - ssynowiec
  14. 992p. - Dominik Łempicki (kapitan)
  15. 968p. - Anonim 3619784
Szczegóły i pełne wyniki

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...