• 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

Object Storage Arubacloud
0 głosów
308 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ź 88 wizyt
pytanie zadane 16 października 2023 w C# przez kubekszklany Gaduła (3,190 p.)
+1 głos
1 odpowiedź 679 wizyt
pytanie zadane 24 sierpnia 2016 w C i C++ przez MrRozgunek Użytkownik (810 p.)
0 głosów
0 odpowiedzi 378 wizyt
pytanie zadane 5 listopada 2020 w C i C++ przez disaster Bywalec (2,120 p.)

92,565 zapytań

141,416 odpowiedzi

319,596 komentarzy

61,948 pasjonatów

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.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...