Sortowanie list różnych, choć mającym część wspólną, typów

0

Witajcie Bracia i Siostry w kodzie

Potrzebuję podpowiedzi do następującego zagadnienia - mam QList<const InventoryItem*> którą mogę w niestandardowy(tj. oparty o moją własną kolejność) sposób posortować poprzez CustomSortOrder::sortItems(QList<const InventoryItem*> &listToSort, const QString &sortGroupName). Teraz zaszła potrzeba, aby tegoż samego CustomSortOrder użyć do posortowania innej QList<const EquipmentGroupItemWidget*>. Ówże EquipmentGroupItemWidget zbudowany jest w takowy sposób:

class EquipmentGroupItemWidget
{
  //jakaś zawartość klasy, ale przede wszystkim:
  InventoryItem *item;
};

Moje pytanie brzmi - czy poprzez użycie jakiejś sprytnej konwersji automatycznej dałoby radę użyć istniejącej implementacji CustomSortOrder do posortowania listy EquipmentGroupItemWidgetów?

2

Możesz w tych 2 klasach po przez interfejs wydzielić pola, które wykorzystujesz do sortowania (bardziej elastyczne rozwiązanie) lub stworzyć klase abstrakcyjną która ma to pole i dziedziczyć po niej te 2 inne.

1

Możesz sobie zamienić swoją customową funkcję sortującą w szablon, w rodzaju

<template T>
CustomSortOrder::sortItems(QList<const T*> &listToSort, const QString &sortGroupName)

Oraz napisać dla obu typów InventoryItem i EquipmentGroupItemWidget metodę, która będzie zwracała różne rzeczy w zależności od typu:

class InventoryItem {
    ...
    InventoryItem* get_inventory_item() { return this; }
};

class EquipmentGroupItemWidget {
    InventoryItem *item;
    ...
    InventoryItem* get_inventory_item() { return item; }
};

Tak że w implementacji sortowania możesz pobrać item z różnych miejsc dla różnych typów:

<template T>
CustomSortOrder::sortItems(QList<const T*> &listToSort, const QString &sortGroupName) {
    // ...
    T* item_handler = ...
    InventoryItem *item = item_handler->get_inventory_item();
    // ...
}

Jak nie chcesz sobie zaśmiecać klas InventoryItem i EquipmentGroupItemWidget to możesz to też wydzielić do specjalizowanego szablonu do pobierania itemu dla różnych typów.

template <class T>
InventoryItem* get_inventory_item(T* item) {
    return nullptr;
}

template <>
InventoryItem* get_inventory_item<InventoryItem>(InventoryItem* inventory_item) {
    return inventory_item;
}


template <>
InventoryItem* get_inventory_item<EquipmentGroupItemWidget>(EquipmentGroupItemWidget* equipment_group_item_widget) {
    return equipment_group_item_widget->item;
}

<template T>
CustomSortOrder::sortItems(QList<const T*> &listToSort, const QString &sortGroupName) {
    // ...
    T* item_handler = ...
    InventoryItem *item = get_inventory_item<T>(item_handler);
}
0

Wielkie dzięki Bracie @Spearhead za podpowiedź. Sam wczoraj doszedłem do wniosku że tu pewnie bez szablonu się nie obejdzie, a Twój post utwierdziła mnie w przekonaniu.

Udało się, bangla i hula. Zrobiłem to sposobem z dodaniem const *InventoryItem getItem() const i przerobieniem metody sortItems() na szablonową.
Jako bonus, prócz obsługi sortowania InventoryItemów i EquipmentGroupItemWidgetów to mogłem ładnie uprościć takie cuś

        auto storage = WeaponsDatabase::getInstance().getSpecificWeapons(weaponType);//WeaponData dziedziczy InventoryItem
        CustomSortOrder::sortItems<InventoryItem>(reinterpret_cast<QList<const InventoryItem*>&>(storage), categoryName);

do

        auto storage = WeaponsDatabase::getInstance().getSpecificWeapons(weaponType);
        CustomSortOrder::sortItems<WeaponData>(storage, categoryName);

Mało tego, widzę potencjał na ewentualne rozszerzenie tego na przyszłość. Ot zdefiniuję sobie

class ISortable
{
 public:
  virtual const InventoryItem* getItem() const = 0;
}

i każda kolejna klasa jaka będzie potrzebować być sortowana tym CustomSortOrderem będzie musiała odziedziczyć i zaimplementować ów interfejs. Coś w ten deseń zdaje się Brat @hzmzp miał na myśli.

1

Wprowadzenie interfejsu ISortable może Ci się wydać niewinne, ale w rzeczywistości to bardzo wiele zmienia i różnica w czasie wykonania jest znaczna. quick-bench pokazuje 5-krotną różnicę w czasie wykonania: https://quick-bench.com/q/He9wVdR59PSZlNd0iWrvqRhmkCU

Zauważ, że nie ma żadnej różnicy między definicją getItem a getItemNonVirtual w InventoryItem, a różnica w czasie wykonania między sortowaniami, które ich używają jest już znaczna.

class InventoryItem : public ISortable
{
public:
  explicit InventoryItem( int i ) : index( i ) {}

  int index;

  const InventoryItem* getItem() const { return this; }

  const InventoryItem* getItemNonVirtual() const { return this; }
};

Trzeba na to bardzo uważać.

Aby zniwelować ten problem, musisz zdewirtualizować wywołanie funkcji getItem() dla InventoryItem słowem kluczowym final:

class InventoryItem : public ISortable
{
...
  const InventoryItem* getItem() const final { return this; }
...
};

Tylko trzeba o tym pamiętać, a wprowadzając nową klasę, która implementuje interfejs ISortable można o tym "zapomnieć".
Wtedy oba sortowania są porównywalne: https://quick-bench.com/q/bKizuHcgMFE4ay7ku7oB7l7VEQk

Ale i tak, posiadanie takiego interfejsu, który ma wirtualną funkcję get... zachęca do użycia go w zły sposób.

0

Interesujące, dzięki Bracie @mwl4
W sumie na moje potrzeby mogę sobie dać adnotację jakbym do czegoś jeszcze owego CustomSortOrder potrzebował, że typ danych w liście/wektorze który ma być posortowany ma zawierać const InventoryItem* getItem() const

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