Klasa iteratora a binary compatibility

0

Czy mając swoją klase kontenera a w niej klase iteratora np

class Container
{
public:

    class iterator{};
    class const_iterator{};
    
private:
    QScopedPointer<ContainerPrivate> d;
};

jeżeli kiedyś usune lub zmienie iterator to złamie kompatybilność na poziomie binarnym?

0

Przy usunięciu to raczej oczywiste ale co przy zmianie? inny typ, implementacja?

1

Nie wiem jakie są gwarancje, ale wydaje mi się, że nie powinno się nic popsuć. Nie dodajemy ani nie usuwamy żadnego elementu składowego który miałby reprezentację binarną w obiekcie klasy Container - a mówimy w końcu o binarnej kompatybilności. Taką reprezentację mają pola i funkcje wirtualne, ale zagnieżdżona klasa istnieje przecież tylko na etapie kompilacji.

Zupełnie co innego gdybyś miał pole (choćby prywatne) typu iterator w klasie Container - to z pewnością zmieni ABI, bo się zmieni sizeof. Również użycie typu iterator jako parametru albo wartości zwracanej funkcji wirtualnej będzie miało wpływ.

Podsumowując: w podanym przypadku usunięcie iteratorów na 90% nie zaszkodzi.

0

Dzięki Ci wielkie @Azarien

Taką reprezentację mają pola i funkcje wirtualne

a zwykłe funkcje?
Bo gdybym kiedyś usunął klasę iteratora to razem z nim funkcje typu begin() end() czy cbegin() i cend() dla wersji const.

1

Za mało danych.
O kompatybilności binarnej można mówić, jeśli masz jakąś bibliotekę, która jest używana w innym projekcie.
Teraz zmian wprowadzane w publicznym API takiej bibliotece nie może być dowolne.

Przykładowo:

  • wielkość klasy nie może się zmieniać
  • kolejność i ilość metod wirtualnych nie może się zmieniać
  • itp

Teraz w twoim przykładzie nie wiadomo, czy to jest klasa z publicznego API biblioteki, a jeśli tak to w jaki sposób się to odbywa.
Tak samo nie opisałeś jakiej natury ma być to zmiana, więc zupełnie nie wiadomo, czy ta zmiana jest dopuszczalna czy nie.

Z tym 90% NIE jakie obstawia @Azarien to byłbym ostrożny. Brak konkretów, więc nie można udzielić odpowiedzi.
Ten iterator na pewno użyłeś w jakimś kontekście i od tego zależy, czy można go bezpiecznie usunąć czy nie.
Zakładając, że pokazałeś wszystko, to usunięcie tego iteratora nie przynosi żadnych konsekwencji, ale mam przeczucie, że to nie wszystko.

Jako, że piszesz w Qt to powinieneś przeczytać coś takiego: https://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B#Note_about_ABI

0

Ok więc trochę bardziej konkretny przykład. Klasa container

class Container
{
public:
    class iterator{};
    class const_iterator{};
    
    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

    const_iterator constBegin() const;
    const_iterator constEnd() const;
    
    // other stuff / public api for container
    
private:
    QScopedPointer<ContainerPrivate> d;
    
}; 

I teraz powiedzmy że w wersji 1.x biblioteki chce usunąć możliwość iterowania po mojej klasie kontenera więc usuwam wewnętrzne klasy iterator i const_iterator i funkcje begin() end() bo nie są już potrzebne.

Używam techniki PIMPL więc wszystkie dane mam pod wskaźnikiem w prywatnej klasie ( jak w przykładzie ), żadnych dodatkowych prywatnych pól.

Mogę w takim przypadku usunąć kiedyś feature iterowania po mojej klasie bez złamania kompatybilności binarnej? bo to że złamie kompatybilność na poziomie źródeł to oczywiste ale to nie jest problemem.

Ważne by klient nie musiał kompilować od nowa projektu z moją biblioteką jak będzie chciał użyć nowej / poprawionej wersji.

0

Tak dokładnie wygląda klasa iteratora

    using value_type  = Data
    using inner_range = QHash<QString, value_type>;
    using outer_range = QHash<QString, inner_range>;

    class iterator : public std::iterator_traits<inner_range::iterator>
    {

    public:
        //using iterator_category = std::forward_iterator_tag;
        iterator(outer_range::iterator const & outer_iterator,
                 outer_range::iterator const & outer_end);

        iterator & operator++();
        reference operator*() const;
        pointer operator->() const;
        bool operator==(iterator const &other) const;
        bool operator!=(iterator const &other) const;

    private:
        outer_range::iterator outer_iterator, outer_end;
        inner_range::iterator inner_iterator, inner_end;
        void update();
    };

i np w funkcji begin

return iterator(d->data.begin(), d->data.end());
0

Nadal niepełne dane!
Wygląda na to, że klasa Container nie jest eksportowana (nie widzę żadnego słowa kluczowego mówiącego o importowaniu lub eksportowaniu klasy lub funkcji), więc nie może być użyta przez użytkownika biblioteki, a w takim wypadku hulaj dusza nie ma problemu.
Jeśli jednak ta klasa jest eksportowana, to tak naprawdę ty chcesz usunąć nie tyle sam klasę iterator, ale funkcje go zwracające, a to jest niedozwolone!

Przypuszczalnie zadałeś złe pytanie.
Czy masz bibliotekę zewnętrzną dynamiczną (dll)? Wtedy binary compability ma znacznie!
Czy ty po prostu udostępniasz źródła lub statyczną bibliotekę i potrzebujesz utrzymywać tylko source compability?

0

Jeśli w tym "other stuff" nie używasz iteratorów, to możesz je usunąć, ale to dziwny pomysł.
Skoro user miał iteratory, to znaczy że mógł ich używać. Chcesz mu je zabrać mówiąc "nie musisz rekompilować projektu ale iteratory won".

Słowem: zrywasz z kompatybilnością a martwisz się o konieczność rekompilacji.

0

@MarekR22 ta klasa w przykładzie nie ma żadnego słowa export ale oczywiście będzie miała - bedzie to projekt biblioteki dynamicznej w Qt więc pewnie coś ala

LIBSHARED_EXPORT

przed nazwą każdej klasy.

@Azarien masz racje to faktycznie dziwny pomysł. Czyli jak coś do biblioteki dodam na starcie to lepiej żeby to wszystko już tam siedziało. Tak będzie chyba najbezpieczniej.

Dzięki wszystkim.

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