Zmienna globalna pomiedzy dwoma projektami

0

Hej,
Pisze w c++ program na studia, mam Visual Studio i projekt A ktory ma referencje do innego projektu B ktory jest biblioteka .lib.

I teraz chce stworzyc zmienna globalna aby funkcji z jednego i drugiego projektu mogly jej uzywac. Dla uproszczenia niech na razie w ogole nie bedzie plikow naglowkowych a deklaracje beda w plikach cpp.

W ktorym projekcie powinna byc definicja takiej funkcji a w ktorym tylko deklaracja tak abym mogl w funkcjach nalezacych do obu projektow sie do nich odnosic?

Z gory dziekuje,
pozdrawiam

6

W ktorym projekcie powinna byc definicja takiej funkcji a w ktorym tylko deklaracja tak abym mogl w funkcjach nalezacych do obu projektow sie do nich odnosic?

W żadnym bo to jest jakiś tragiczny pomysł. W ogóle zmienne globalne to słaby pomysł a współdzielenie ich w taki sposób to już zakrawa o szaleństwo. Możesz wyjaśnić co chcesz w ten sposób osiągnąć? Bo na pewno nie idziesz dobrą drogą.

1

Tak jak poprzednik napisał, jak masz pomysł na zmienną globalną, znaczy się powinieneś raz jeszcze przemyśleć projekt, bo jest zły. Zmienne globalne winno się używać w wyjątkowych przypadkach.

0

Chce sie nauczyc pracy pomiedzy projektami i zrozumiec zaleznosci ktore sa miedzy nimi i jak dobrze rozpoznawac takie zaleznosci. bo mam duzo roznych projektow powiazanych ze soba w jednej solucji i mam problem z bledami linkera gdy probuje dolaczac biblioteki z innych projektow i wywolywac z nich funkcje. Moja wiedza jest teraz na poziomie jednego projektu gdzie mam pliki .cpp ktore sa kompilowane do plikow .o, i linkowane statycznie z pojedynczymi bibliotekami .lib wtedy rozumiem co sie dzieje.

Problem mam gdy sprawa sie komplikuje i projektow jest duzo, wszystkie maja zaleznosci miedzy soba. Czesc jest poustawiana jako referencje do innych projektow, czesc jako dependencje, a czesc bibliotek po prostu dodana we wlasciwosciach jako inputy do linkera.

Mam sytuacje taka ze mam referencje do projektu ktory jest libem, a gdy chce uzyc z niego jakiejs zmiennej lokalnej to wysypuje mi sie masa bledow kompilacji. chce zrozumiec czego jest to wynikiem.

0

Nie znam Visuala, natomiast jak już musisz tworzyć zmienną globalną:
http://stackoverflow.com/questions/496448/how-to-correctly-use-the-extern-keyword-in-c ;)

0

Ale ja wiem jak ją stworzyć w obrębie jednego projektu i kilku plików cpp tylko problem mam z dependencjami. Więc może sprecyzuje moje pytania.

Jeżeli mam dwa projekty i pierwszy z nich tworzy plik binarny .exe a drugi statyczną bibliotekę .lib. I oba te projekty są w jednej solucji to jakie są fizycznie możliwości aby wywołać funkcje albo odnosić się do zmiennych z drugiego projektu w pierwszym projekcie?

Na pewno jednym sposobem jest include pliku nagłówkowego i dodanie tego liba we właściwościach dla pierwszego projektu. Ten sposób zadziała nawet jak bym nie miał źródeł tylko sam plik lib.

A czy to że mam projekty oba w solucji umożliwia jakieś bezpośrednie zlinkowanie plików .o bez pośredniego tworzenia pliku .lib?
W visual studio mogę ustawiać dwie rzeczy: dependencje między projektami w solucji oraz referencje.

Rozumiem to tak że jeżeli ustawię dependencje mojego projektu .exe do projektu biblioteki to on najpierw zbuduje bibliotekę a potem exeka. Jak to jest w przypadku ustawienia referencji?

0

@tytrydsdf trochę kombinujesz. Skoro masz plik *.lib to nie ma innej metody aby dodać do innego projektu tego liba wraz z plikiem *.h. Jeśli chcesz łączyć pliki *.o to połącz oba projekty w jeden i nie będziesz miał tego problemu.

Swoją drogą to rozwiązanie z libami jest wygodniejsze ponieważ jednej libki można używać nawet w 100 projektów i nie kompilujesz tego samego kodu 100 razy, tylko jeden tworząc nową wersję *.lib.

Co do samej zmiennej globalnej to zrobienie takiej zmiennej w lib'ce powoduje to, że ona będzie również globalna w pliku exe. Dlatego nie warto z tego rozwiązania korzystać, bo przy większej liczbie projektów łatwo i konflikt nazw.

0
Mr.YaHooo napisał(a):

@tytrydsdf trochę kombinujesz. Skoro masz plik *.lib to nie ma innej metody aby dodać do innego projektu tego liba wraz z plikiem *.h. Jeśli chcesz łączyć pliki *.o to połącz oba projekty w jeden i nie będziesz miał tego problemu.

Na razie chcę tylko użyć zmiennej globalnej i żeby mi to zadziałało. Jak to będę już miał to będę myślał jak się jej pozbyć i zastąpić czymś innym.

Problem mam w tym że jeżeli dodaję tego liba i chcę wywołać jakąś funkcję, to nagle mam masę błędów linkera o nieznalezionych symbolach. W solucji widzę że projekt tego liba zależy od kilku innych projektów. I czy teraz te wszystkie biblioteki które były potrzebne do stworzenia tego liba muszę pododawać również do mojego projektu?

1
tytrydsdf napisał(a):

Problem mam w tym że jeżeli dodaję tego liba i chcę wywołać jakąś funkcję, to nagle mam masę błędów linkera o nieznalezionych symbolach. W solucji widzę że projekt tego liba zależy od kilku innych projektów. I czy teraz te wszystkie biblioteki które były potrzebne do stworzenia tego liba muszę pododawać również do mojego projektu?
Ale przecież linker pokazuje Ci dokładnie jakich funkcji/klas nie widzi i Ty wiesz w jakiej libce one są, ja tego nie wiem bo sypiesz ogólnikami bez konkretnych nazw itp. Dla próby dodaj do głównego projektu również liby których używa dołączany do exe'ca lib i się okaże...

0

Hmmm... użyj po prostu wzorca projektowego Singleton. A odradzanie zostawiam innym bo widzę, że mają problemy z tym, że ktoś się uczy O_o

1

Masz projekt A, który się buduje do exe. Masz projekt B, który w pewnym sensie rozszerza to exe.

Teraz musisz się zastanowić, w jaki sposób chcesz zbudować projekt B. Czy po prostu jako statycznego liba, czy dll z eksportowanymi klasami, czy klasyczną dll.

Statyczny lib - to już ponoć wiesz.
DLL z eksportowanymi klasami - klasy, które mogą być wykorzystywane w pliku exe, muszą być wyeksportowane. Czyli podczas budowania projektu B te klasy muszą być oznaczone jako eksportowane, a w projekcie A jako importowane. Robi się to za pomocą odpowiedniego makra (w uproszczeniu):

Plik h:

 
#ifdef PROJECTB_EXPORTS
  #define PROJECTB_API __declspec(dllexport)
#else
  #define PROJECTB_API __declspec(dllimport)
#endif

class PROJECTB_API MojaKlasa
{
//tu normalnie DEKLARUJESZ klasę, jak w typowym pliku h.
}

Następnie w projekcie B dodajesz w ustawieniach projektu dyrektywę PROJECTB_EXPORTS (Visual zazwyczaj doda Ci coś takiego automatycznie, jeśli tworzysz dll).

Dzięki temu projekt B będzie wiedział, że klasę ma eksportować, a projekt A (gdzie includujesz ten plik h) będzie wiedział, że klasę ma importować (bo nie ma nigdzie dyrektywy PROJECTB_EXPORTS).

A potem w projekcie A możesz się posługiwać taką dllką, jakby była zwykłą statyczną biblioteką:

 
MojaKlasa obj = new MojaKlasa();

Ta metoda ma swoje plusy i minusy. Plusy są takie, że masz dll, którego możesz używać jak liba.
Minusy:

  • nie możesz w klasie jawnie posługiwać się standardową biblioteką. Tzn. nie możesz zrobić czegoś takiego:
class PROJECTB_API MojaKlasa
{
  void print(const std::wstring & msg);
}

Musisz np.:

class PROJECTB_API MojaKlasa
{
  void print(const wchar_t * pMsg);
}

Lub jakaś własna obsługa stringa. Nie możesz w taki sposób przekazywać, ponieważ wersje biblioteki standardowej różnią się i dla każdego kompilatora są nieco inne binarnie. Oznacza to, że jeśli projekt B zbudujesz w jednym kompilatorze (np. VisualStudio 2012) i będziesz chciał go używać z poziomu projektu utworzonego w innym kompilatorze (np. VisualStudio 2013), to pojawią się błędy w działaniu związane z pamięcią. W ogóle taki kod chyba nawet nie skompiluje się poprawnie. Tzn. jeśli klasa jest eksportowana, to wszystkie jej składniki też muszą być oznaczone jako eksportowane. Obiekty biblioteki standardowej takie nie są.

Nie wiem też, czy można taką dllkę zastosować bez problemów w innym języku, niech się ktoś wypowie.

Masz jeszcze trzecią możliwość. Klasyczna dllka.
W pliku h umieszczasz deklarację jakiegoś interfejsu.
Umieszczasz też funkcję, która utworzy Ci obiekt tego interfejsu i kolejną, która go zniszczy. W skrócie to mogłoby wyglądać tak:

class IPrintable
{
  public:
      virtual void PrintMsg(const wchar_t * pMsg) = 0;
      virtual void PrintError(const wchar_t * pMsg) = 0;
};

extern "C" bool __stdcall CreateObject(IPrintable *& pObj);
extern "C" void __stdcall DestroyObject(IPrintable *& pObj);
}
 

Gdzieś w jakimś pliku cpp w projekcie B deklarujesz klasę, która dziedziczy po IPrintable i te funkcje.

Potem w execu jest już trochę trudniej. Musisz załadować taką bibliotekę (LoadLibrary), a następnie odnaleźć określone funkcje (GetProcAddress), itd. Odsyłam tu do netu, bo i tak się dość mocno rozpisałem :) Pamiętaj jednak, że poza takimi deklaracjami, powinieneś zrobić odpowiednie wpisy w pliku def, bo c++ Ci i tak zmieni nazwy funkcji mimo jawnie zastosowanej konwencji (__stdcall).

Generalnie w żadnym z wypadków nie powinieneś eksportować żadnej funkcji, która w argumencie przyjmuje typ z biblioteki standardowej. O tym już pisałem. W ostatnim przypadku pokazałem Ci też, że to projekt B musi zatroszczyć się o stworzenie i zniszczenie eksportowanego obiektu. Z tego samego powodu. Alokacja i dealokacja pamięci różnią się binarnie pomiędzy różnymi kompilatorami.

Teraz jeśli jeden projekt (A) korzysta w jakiś sposób z drugiego (B), to projekt A powinien być zależny od B (dependency), co spowoduje, że projekt B zbuduje się zanim zacznie budować się projekt A. I będziesz też musiał dodać do inputa w projekcie A liba utworzonego na podstawie projektu B(chyba, że projekt B to klasyczna dllka, wtedy linker Cię nie interesuje).

Mam nadzieję, że nie zamotałem za bardzo i wiesz o co chodzi.

0

@pylaochos ale zdajesz sobie sprawę z tego, że proponowanie singletona tylko po to aby uniknąć zmiennej globalnej też nie jest dobrym rozwiązaniem? Dla mnie samo użycie singletona powinno być motywowane czymś innym niż niechęć do zmiennych globalnych.

@Juhas całkiem zgrabnie rozpisane różnice. Ja osobiście używam 3 wersji z dynamiczną dll'ką. Daje to największe możliwości. Co do eksportowania metod mających argumenty typu std::string to jest to możliwe, jednak trzeba pilnować aby wszystkie projekty były budowane z użyciem tej samej wersji środowisk, co na dobrą sprawę nie jest jakieś trudne do użycia. Jednak eliminuje wtedy możliwość użycia takiej dll'ki w innym środowisku, nie mówiąc już o innym języku.

1 użytkowników online, w tym zalogowanych: 0, gości: 1