Microsoft + template + DLL = kłopoty

Wyobraźmy sobie, że tworzymy bibliotekę dzieloną (na Windows DLL), w której tworzymy obiekt będący instancją jakiegoś template zawierającego składowe statyczne (ot, choćby vector<string>) i chcemy przekazać tenże obiekt gdzieś na zewnątrz naszej biblioteki (albo na odwrót, otrzymujemy taki obiekt z zewnątrz).

Kilka przykładów takich sytuacji:

  • tworzymy obiekt posiadający metodę const vector<string>& readKeys();
  • albo robimy funkcję void readSth(vector<string>& param);
  • albo funkcję void doSthElse(map<string,int> param);
  • albo metodę map<int,int>::iterator myBegin().

Albo nawet nie wsadzamy nic takiego do API biblioteki ale robimy metodę inline w której wnętrzu operujemy na obiekcie takiego typu, np.

    // Wewnątrz deklaracji klasy XXX 
    map<string, int> m_someMap;
    // ...
    void SetParam(const string& key, int val)
    {
        m_someMap.insert(make_pair(key, val));
    }

(kod metody zostanie rozwinięty i skompilowany w ramach kodu wołającego modułu - choć nie zawsze, niekiedy tylko w wersji release).

Nasz przyjaciel Microsoft (Visual C++) gwarantuje nam w każdej z tych sytuacji, w chwili wywołania odpowiedniej metody przez główny program lub inną bibliotekę, wygenerowanie gwałtu pamięci.

Co więcej jest to "by design". Opis znajduje się w MSDN jako:

PRB: Access Violation When Accessing STL Object in DLL
Last reviewed: August 18, 1997
Article ID: Q172396

Sugerowane w tym dokumencie obejście to instancjonowanie eksplicite naszych template i eksportowanie ich z tworzonej biblioteki (gorzej gdy zrobią to niezależnie dla tego samego template autorzy dwóch niezależnych bibliotek). Można też oczywiście starannie usuwać wszelkie tego typu odwołania z API bibliotek i zamieniać metody inline na normalne.

Pozwolę sobie zacytować ostateczny komentarz kolegi, który stracił na badanie o co chodzi bodajże paręnaście dni i zdiagnozował ten problem:
Nie nam k...a żyje M$ ^$%^#^%^&$%&$&@#$& !!!!!!

Uwaga końcowa: nie ma takich problemów z np. standardowym stringiem (który też jest rozwinięciem template basic_string) - jego sam Microsoft odpowiednio wyeksportował z biblioteki standardowej.

komentarze obsługiwane przez Disqus