Jest jakiś ekwiwalent dla makra w C++11/14?

1

Czołem Bracia w kodzie

Mam sobie takowe makro:

#define HIDE_HARDPOINT_MACRO(TYPE) QSizePolicy policy = ui->label##TYPE##Icon->sizePolicy();\
    policy.setRetainSizeWhenHidden(true);\
    ui->label##TYPE##HardpointsLeft->setSizePolicy(policy);\
    ui->label##TYPE##Icon->setSizePolicy(policy);\
    ui->labelMax##TYPE##Hardpoints->setSizePolicy(policy);\
    ui->label##TYPE##HardpointsLeft->hide();\
    ui->label##TYPE##Icon->hide();\
    ui->labelMax##TYPE##Hardpoints->hide();

którego sensem istnienia jest podmiana nazwy kontrolki w UI poprzez wywołanie np

HIDE_HARDPOINTS_MACRO(Ballistic);//dostanę wszędzie labelBallisticCośTamDalej->róbCoś(); i o taki efekt mi chodzi.

Moje pytanie brzmi - czy w C++11/14 pojawiła się jakaś realna alternatywa dla takiej operacji? Może owe wychwalane lambdy?

1

Z tego co kojarzę, to niestety nie. Nie ma zamiennika dla łączenia tokenów preprocesorem.

5

Nie mam pojęcia czego ty oczekujesz od C++11/14.
Zresztą to makro jest strasznie dziwne. Mi to wygląda na siłowe rozwiązanie czegoś, co powinno być zrobione w jakiś prostszy sposób.

To wygląda na funkcjonalność UI, która powinna być zrobiona w osobnym widget.
Widget ten już sam powinien obsługiwać zmiany stanów, tak jak ty to chcesz i dopiero ten widget powinien wylądować w oknie głównym.
Wówczas te durne makro staje się zbędne.

0

Prostszy sposób, czyli co? Stworzyć nową klasę tylko po to, żeby wpisać tak czy owak te instrukcje chowające i przełączające politykę przeskalowań dla zestawu QWidgetów? Słabe.

3

@MasterBLB nie. Tworzenie nowej klasy z nową funkcjonalnością nie jest słabe. Słabe to jest tworzenie takich makr. Potem weź to zdebuguj wykonując krok po kroku. Makro powinno być jak najprostsze, a nie zawierać w sobie 8 linijek kodu...

0

Nie muszę debugować.Są to biblioteczne funkcje Qt, więc co do nich raz mogę mieć pewne zaufanie to raz, a dwa jako że dotyczą GUI to wszelkie nieprawidłowości od razu zauważę. Nie wspominając o tym, że kod przepisałem na makro dopiero po sprawdzeniu, czy działa.

Aktualny wygląd makra

void MechPartWidget::setHardpointsNumber(int ballistic, int missile, int energy, int support)
{
    //remember number of hardpoints
    hardpointsCount[HardpointTypes::Ballistic] = ballistic;
    hardpointsCount[HardpointTypes::Missile] = missile;
    hardpointsCount[HardpointTypes::Energy] = energy;
    hardpointsCount[HardpointTypes::Antipersonnel] = support;

#define SETUP_HARDPOINTS_MACRO(TYPE, type) if (type == 0)\
    {\
        ui->label##TYPE##HardpointsLeft->hide();\
        ui->label##TYPE##Icon->hide();\
        ui->labelMax##TYPE##Hardpoints->hide();\
    }\
    else\
    {\
        QString number(QString::number(type));\
        ui->label##TYPE##HardpointsLeft->setText(number);\
        ui->labelMax##TYPE##Hardpoints->setText('/' + number);\
    }

    //setup non-existing hardpoint icon and numbers
    SETUP_HARDPOINTS_MACRO(Ballistic, ballistic);
    SETUP_HARDPOINTS_MACRO(Energy, energy);
    SETUP_HARDPOINTS_MACRO(Missile, missile);
    SETUP_HARDPOINTS_MACRO(Support, support);
#undef SETUP_HARDPOINT_MACRO
}

według was, nie tylko musiałbym dołożyć nową klasę, nie tylko zapewnić jej dostęp do ui, ale koniec końców i tak gdzieś tam musiałby się znaleźć wieżowiec

    if (ballistic == 0)
    {
        ui->labelBallisticHardpointsLeft->hide();
        ui->labelBallisticIcon->hide();
        ui->labelMaxBalllisticHardpoints->hide();
    }
    else
    {
        QString number(QString::number(ballistic));
        ui->labelBalllisticHardpointsLeft->setText(number);
        ui->labelMaxBalllisticHardpoints->setText('/' + number);
    }
    if (energy == 0)
    {
        ui->labelEnergyHardpointsLeft->hide();
        ui->labelEnergyIcon->hide();
        ui->labelMaxEnergyHardpoints->hide();
    }
    else
    {
        QString number(QString::number(energy));
        ui->labelEnergyHardpointsLeft->setText(number);
        ui->labelMaxEnergyHardpoints->setText('/' + number);
    }

    if (missile == 0)
    {
        ui->labelMissileHardpointsLeft->hide();
        ui->labelMissileIcon->hide();
        ui->labelMaxMissileHardpoints->hide();
    }
    else
    {
        QString number(QString::number(missile));
        ui->labelMissileHardpointsLeft->setText(number);
        ui->labelMaxMissileHardpoints->setText('/' + number);
    }

    if (support == 0)
    {
        ui->labelSupportHardpointsLeft->hide();
        ui->labelSupportIcon->hide();
        ui->labelMaxSupportHardpoints->hide();
    }
    else
    {
        QString number(QString::number(support));
        ui->labelSupportHardpointsLeft->setText(number);
        ui->labelMaxSupportHardpoints->setText('/' + number);
    }

hmmm jednakże można by załatwić sprawę funkcją:

void setupHardpointsGUI(int number, QLablel *left, QLabel *icon, QLabel *max)
{
   if (number == 0)
   {
      left->hide();
      max->hide();
      icon->hide();
   }
   else
   {
     QString n(QString::number(number));
     left->setText(n);
     max->setText('/' + n);
   }
}

I zastąpić wywołanie makr takimi

setupHardpoint(ballistic, ui->labelBallisticHardpointsLeft, ui->labelBallisticIcon, ui->labelMaxBalllisticHardpoints);
setupHardpoint(energy,  ui->labelEnergyHardpointsLeft, ui->labelEnergyIcon, ui->labelMaxEnergyHardpoints);

W sumie, podoba mi się.

1

No jasne, jak piszesz kod dla siebie, to możesz równie dobrze nie robić żadnej klasy, ani nawet funkcji. Ekwiwalentem dla makra w C++ jest ich nieużywanie. Od tego są funkcje inline, lambdy i funktory.

#define SETUP_HARDPOINTS_MACRO(TYPE, type) if (type == 0)\
    {\
        ui->label##TYPE##HardpointsLeft->hide();\
        ui->label##TYPE##Icon->hide();\
        ui->labelMax##TYPE##Hardpoints->hide();\
    }\
    else\
    {\
        QString number(QString::number(type));\
        ui->label##TYPE##HardpointsLeft->setText(number);\
        ui->labelMax##TYPE##Hardpoints->setText('/' + number);\
    }

takie coś można zapisać jako funkcję z jednym parametrem i wywoływać ją gdzie potrzeba - wygląda czytelniej i nie syfi kodu.

2

Lol nie.

class Hardpoints
{
public:
    void setup(const int type) // zamiennik dla: SETUP_HARDPOINTS_MACRO()
    {
        m_count = type;
        if(type == 0)
        {
            m_labelHardpointsLeft->hide();
            m_labelIcon->hide();
            m_labelMaxHardpoints->hide();
        }
        else
        {
            const QString number(QString::number(type));
            m_labelHardpointsLeft->setText(number);
            m_labelMaxHardpoints->setText('/' + number);
        }
    }

    void hide() // zamiennik dla: HIDE_HARDPOINT_MACRO()
    {
        QSizePolicy policy = m_labelIcon->sizePolicy();
        policy.setRetainSizeWhenHidden(true);
        m_labelHardpointsLeft->setSizePolicy(policy);
        m_labelIcon->setSizePolicy(policy);
        m_labelMaxHardpoints->setSizePolicy(policy);
        m_labelHardpointsLeft->hide();
        m_labelIcon->hide();
        m_labelMaxHardpoints->hide();
    }

private:
    HardpointTypes m_type;
    int m_count;
    QLabel *m_labelHardpointsLeft;
    QLabel *m_labelIcon;
    QLabel *m_labelMaxHardpoints;
};

class MechPartWidget
{
private:
    Hardpoints m_ballistic;
    Hardpoints m_missile;
    Hardpoints m_energy;
    Hardpoints m_support;

    // Inicjalizacja to twoja broszka (możesz zaincjalizować wskaźnikami z ui-> jak chcesz...)
    // ... inne deklaracje i definicje
};

void MechPartWidget::setHardpointsNumber(int ballistic, int missile, int energy, int support)
{
    m_ballistic.setup(ballistic);
    m_missile.setup(missile);
    m_energy.setup(energy);
    m_support.setup(support);
}
1

Lol nie.
Wszystko czego potrzebuję to w trakcie inicjalizacji obiektu MechPartWidget to zapamiętać przesłane wartości ballistic, missile, energy i support, a jeśli któraś z nich jest 0 pochować ilustrujące je elementy GUI - potem stan klasy nie będzie się już zmieniał. I co mam, wpierdzielić teraz nową klasę tylko po to? Przegięcie pały Bracie...ale, już wiem skąd się biorą zryte projekty z piedylionem zbytecznych klas skoro takie rozwiązanie uważane jest za dobre.

Niemniej, dyskusja z Wami naprowadziła mnie na chyba pierwsze w życiu sensowne zastosowanie do czegoś lambdy :] Już wczoraj zauważyłem, iż mogę użyć zamiennika funkcyjnego:

void setupHardpointsGUI(int count, QLablel *left, QLabel *icon, QLabel *max)
{
   if (count == 0)
   {
      left->hide();
      max->hide();
      icon->hide();
   }
   else
   {
     QString n(QString::number(count));
     left->setText(n);
     max->setText('/' + n);
   }
}

jest to OK, ale nie podobało mi się, że sobie trochę zaśmiecę interfejs klasy metodą, która będzie użyta tylko 4 razy w jednej metodzie wywoływanej jedynie raz - generalnie lubię minimalizm. Ale w końcu znalazłem niewymuszone użycie dla lambdy ^^

void MechPartWidget::setHardpointsNumber(int ballistic, int missile, int energy, int support)
{
    //remember number of hardpoints
    hardpointsCount[HardpointTypes::Ballistic] = ballistic;
    hardpointsCount[HardpointTypes::Missile] = missile;
    hardpointsCount[HardpointTypes::Energy] = energy;
    hardpointsCount[HardpointTypes::Antipersonnel] = support;

    auto setupHardpoint = [](int count, QLabel *left, QLabel *icon, QLabel *max)->void
    {
       if (count == 0)
       {
          left->hide();
          max->hide();
          icon->hide();
       }
       else
       {
         QString n(QString::number(count));
         left->setText(n);
         max->setText('/' + n);
       }
    };

    setupHardpoint(ballistic, ui->labelBallisticHardpointsLeft, ui->labelBallisticIcon, ui->labelMaxBallisticHardpoints);
    setupHardpoint(missile, ui->labelMissileHardpointsLeft, ui->labelMissileIcon, ui->labelMaxMissileHardpoints);
    setupHardpoint(energy, ui->labelEnergyHardpointsLeft, ui->labelEnergyIcon, ui->labelMaxEnergyHardpoints);
    setupHardpoint(support, ui->labelSupportHardpointsLeft, ui->labelSupportIcon, ui->labelMaxSupportHardpoints);
}

I to by było tyle w temacie dodawania nowej klasy do projektu :]

1

@mwl4:
KISS => no tak, wszak dodanie nowej klasy, z minimum 2 funkcjami składowymi (bo możliwe że jakiś konstruktor i destruktor też byłyby potrzebne) oraz 5 składnikami, dodatkowo wymaganie inicjalizacji jej instancji w MechPartWidget jest przecież tak bardzo prostsze od jednej lambdy zdefiniowanej dokładnie w tym miejscu, gdzie jest potrzebna.
DRY => i dlatego powstało makro HIDE/SETUP_HARDPOINTS_MACRO aby uprościć wieżę if-ów. Załatwiało wprawdzie sprawę w zadowalający mnie sposób, jednak mając do dyspozycji C++11 chciałem poszukać jakiejś lepszej alternatywy. Stąd powstał ten temat, niestety wszyscyście popłynęli z wymyślaniem coraz to bardziej skomplikowanych rozwiązań prostego problemu, ale koniec końców sam sobie odpowiedziałem.
YAGNI => Jak widzisz Bracie nie potrzebujemy Twojej klasy aby uzyskać pożądany efekt, którym jest jednokrotnie przeprowadzona inicjalizacja agregatu MechPartWidget. W innych miejscach kodu też nie ma potrzeby zastosowania klasy Hardpoints póki co.ALE! Tutaj przyznaję częściowo rację, jeśli potrzeba taka kiedyś zajdzie wówczas warto będzie rozważyć jej dodanie, i ewentualnie zrefaktorować kawałek kodu używający lambdy.

2
[MasterBLB napisał(a)](https://4programmers.net/Forum/1536544

Moje pytanie brzmi - czy w C++11/14 pojawiła się jakaś realna alternatywa dla takiej operacji? Może owe wychwalane lambdy?

Nie. C++, poza prymitywnym systemem makr z C, nie posiada kompletnego systemu metaprogramowania. Makra z C to nadal najpotężniejsza rzecz, którą oferuje w tym zakresie.

W innych językach takie coś byś ogarnął refleksją czasu wykonania albo makrami i byłoby to prawidłowe i zalecane podejście.

Wydaje mi się, że w tej sytuacji Twoje rozwiązanie z makrem C jest najprostsze, choć przez ułomność makr z C może nie być lubiane przez programistów C++.

0

Pięknie dziękuję Bracie @Krolik za objaśnienie, i wyrazy szacunku za odpowiedź na postawione pytanie zamiast zarzucanie mnie tekstami o konieczności tworzenia nowej klasy.
Co prawda jak się okazało owo makro dało się zastąpić lambdą, ale jakiś wybitnych korzyści z tego tytułu nie uświadczyłem, poza dydaktycznymi.

1
Krolik napisał(a):
[MasterBLB napisał(a)](https://4programmers.net/Forum/1536544

Moje pytanie brzmi - czy w C++11/14 pojawiła się jakaś realna alternatywa dla takiej operacji? Może owe wychwalane lambdy?

Nie. C++, poza prymitywnym systemem makr z C, nie posiada kompletnego systemu metaprogramowania. Makra z C to nadal najpotężniejsza rzecz, którą oferuje w tym zakresie.

W innych językach takie coś byś ogarnął refleksją czasu wykonania albo makrami i byłoby to prawidłowe i zalecane podejście.

Wydaje mi się, że w tej sytuacji Twoje rozwiązanie z makrem C jest najprostsze, choć przez ułomność makr z C może nie być lubiane przez programistów C++.

Qt ma meta dane i teoretycznie można by było napisać taki crap code, który zastępuje te makro

void MainWindow::crapyChangeState(QString &typeName)
{
    auto hardpoint = findChild<QLabel *>("label" + typeName + "HardpointsLeft"));
    auto icon = findChild<QLabel *>("label" + typeName + "Icon"));
    auto maxHardpoints = findChild<QLabel *>("labelMax" + typeName + "Hardpoints"));

    auto policy  = icon->sizePolicy();
    policy.setRetainSizeWhenHidden(true);

    hardpoint->setSizePolicy(policy);
    icon->setSizePolicy(policy);
    maxHardpoints->setSizePolicy(policy);

    hardpoint->hide();
    icon->hide();
    maxHardpoints->hide();
}

Nadal jednak uważam, że najlepiej zrobić enkapsulację niż robić te dziwne hacki.

0

@MarekR22:, i co za zysk miałbym z owej enkapsulacji poza skomplikowaniem prostej w sumie czynności? Od razu mówię, iż wszelkie odpowiedzi "ewentualne zmiany w przyszłości itd" w tym konkretnym przypadku oznaczają brak zysku. Tymczasem zastosowanie lambdy:

  • upraszcza chowanie i politykę dla wybranych widgetów, czyli implementuje DRY
  • jak słusznie zostało to zauważone umożliwia śledzenie debuggerem (wprawdzie dla tego przypadku niezbyt wielka to zaleta, no ale zawsze)
  • nie zaśmieca definicji klasy w .h nadmiarową funkcją która tak naprawdę jest zbyteczna (upraszcza definicję klasy, co mi się podoba)
  • mam w końcu jakiś powód aby użyć lambdy (zaleta istotna dla mnie, do tej pory kod na którym pracowałem nie wspierał C++11)
1

@MasterBLB: A skąd ja to mam wiedzieć?
Czy widzę cały twój kod? Czy znam twoje wymagania? Czy wiem jak duży masz projekt? Czy wiem jak duży jest twój zespół?
Jedynie co widzę to dziwne makro i mogę się podzielić swoją opinią względnie swoim doświadczeniem. Jeśli jest ono użyte w 2-3 przysadkach to bym go nie ruszał. Jeśli wiej niż 5 to próbowałbym je zastąpić czyś co było by łatwiejsze w utrzymaniu szczególnie jeśli jest to projekt pisany przez wiele osób.
To co już z tym zrobisz to twoja sprawa i twoja decyzja.

Dla mnie całe pytanie wygląda na problem XY. Patrząc na to co robi te makro, zastanawia mnie czemu je w ogóle potrzebujesz?
Może lepiej zrobić to za pomocą wyspecjalizowanego Widget-a?
A może StyleSheet załatwiło by to w bardziej elegancki sposób?
A może w samym Qt Designer można to wyklinać?
A może te widety, można jakoś inaczej zgrupować w Qt Designer?

Nie wiem, bo nie znam twojego problemu X.

0

We wcześniejszych postach już było wspomniane jakie są wymagania, ale żeby nie było

  • metoda MechPartWidget::setupHardpoints() gdzie jest użyte makro zostanie wywołana wyłącznie raz per instancja klasy. Tych instancji będzie w sumie 8, niezależnych od siebie obiektów.
  • to makro/lambda nie pojawia się w innych częściach projektu
  • klasa MechPartWidget nie jest planowana do dalszego dziedziczenia

mam nadzieję, że teraz jaśniej. Btw, czy owych pytań nie należało zadać przed proponowaniem rozwiązań?

1

To nie są wymagania, ale opis techniczny realizacji tych wymagań!
Wymaganie może wyglądać np tak:
Jako użytkownik końcowy chcę widzieć stan części mechanicznej, w przypadku gdy blablabla .... . W przeciwnym wypadku blablabla .... .

Jeśli w wymaganiach od klienta masz opis jak coś technicznie ma być zrealizowane (a nie ma nic wspólnego z tworzeniem oprogramowania), to najprawdopodobniej klient sam nie wie czego chce, albo dał się komuś złapać na hasła marketingowe.

A z tego co opisałeś wynika, że spokojnie można to wyklinać Qt Designer.
Jeśli dobrze pamiętam, to sizePolicy można tam dostosować.
Jedynie widoczność zależy od zewnętrznych danych, a to realizujesz bez makra.

5
MasterBLB napisał(a):

@MarekR22:, i co za zysk miałbym z owej enkapsulacji poza skomplikowaniem prostej w sumie czynności?

Gdybyś stworzył to w postaci widgeta to taki twór możesz bez problemu używać w Qt Designer jak każdy inny "wbudowany".
A ponieważ cała logika(ukrywanie/chowanie) byłaby w samym widget'cie to kod uprościłby się do:

hardpointWidget->setValue(value);

Do widgeta możesz dodać własne properties, np. minimalną wartość przy której widget jest widoczny, ikonę, którą widget ma użyć, nazwę, etc.
Dodatkowo łatwiej jest takie coś ostylować w CSS.

Nie uważasz, że taki kod wyglądałby całkiem zgrabnie?

void MechPartsWidget::setupHardpoints(const unordered_map<QString,int>& hardpoints)
{
  for (auto& item : hardpoints)
  {
    hardpointWidgets[item.first]->setValue(item.second);
  }
}

A z C++17 to już w ogóle jest miód:

for (const auto& [name,value] : hardpoints)
{
  hardpointWidgets[name]->setValue(value);
}
2

Dobra Bracia programiści @MarekR22 i @tajny_agent, rzecz wygląda następująco - tak wygląda instancja MechDesignWidget:
MechDesignWidget.PNG
a tak jest zaprojektowany w Designerze:
MechDesign in Designer.PNG
Te nazwy w QGroupBoxach + drabinka rysowana na biało + przycisk "Remove stuff" to właśnie jest instancja MechPartWidget, który w Designerze jest zdefiniowany następująco:
MechPartWidget in Designer.PNG
owe QLabele 10/10 + ikona są właśnie przedmiotem makra/lambdy - jeśli w strukturze LocationData pokazanej niżej któryś typ hardpointów jest 0 to wtedy odpowiadające mu 3 widgety mają zostać schowane.

Kod kostruktora MechDesignWidget:

MechDesignWidget::MechDesignWidget(const QString &variantName, QWidget *parent)
: QWidget(parent), ui(new Ui::MechDesign)
{
    ui->setupUi(this);

    //tak, mam świadomość że tu następuje silne powiązanie MechDesignWidget z MechsDatabase które da się uniknąć przesłaniem MechData jako parametr konstruktora
    mech = MechsDatabase::getInstance().getMechData(variantName);

    //dla wygody definicja struktury MechData która oczywiście siedzi gdzie indziej
struct MechData
{
    struct LocationData
    {
        LocationData(const QString &name, int size): locationName(name), inventorySlotsCount(size)
        {}
        LocationData() = default;

        QString locationName;
        int inventorySlotsCount;
        int energyHardpoints = 0;
        int ballisticHardpoints = 0;
        int missileHardpoints = 0;
        int supportHardpoints = 0;
        QVector<const InventoryItem*> inventory;
    };

    QString name;
    QString variantName;
    QString weighClass;
    QString hardpointsAndJumpJetsInfo;
    int totalTonnage;
    int maxJumpJets;
    QVector<LocationData> locations;
};
 
    //setup mech parts - its name,hardpoints number and slots inventory size
    foreach (const MechData::LocationData &location, mech.locations)
    {
        MechPartWidget *mechPartWidget;
        if (location.locationName.compare("Head", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->head;
        }
        else if (location.locationName.compare("LeftArm", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->leftArm;
        }
        else if (location.locationName.compare("LeftTorso", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->leftTorso;
        }
        else if (location.locationName.compare("LeftLeg", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->leftLeg;
        }
        else if (location.locationName.compare("CenterTorso", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->centralTorso;
        }
        else if (location.locationName.compare("RightTorso", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->rightTorso;
        }
        else if (location.locationName.compare("RightArm", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->rightArm;
        }
        else// if (location.locationName.compare("RightLeg", Qt::CaseInsensitive) == 0)
        {
            mechPartWidget = ui->rightLeg;
        }

        mechPartWidget->setupMechPart(location);

        //po staremu było coś w stylu
        //mechPartWidget->setPartName(location.locationName);
        //mechPartWidget->setPartName(location.inventorySlotsCount);
        //mechPartWidget->setupHardpoints(location.energyHardpoints, location.ballisticHardpoints, location.missileHardpoints, location.supportHardpoints
        //mechPartWidget->set
 
    }
}

i teraz MechPartWidget::setupMechPart wygląda tak

void MechPartWidget::setupMechPart(const MechData::LocationData &locationData)
{
    //set name of the part
    QString name = locationData.locationName;
    //Add space between left/right/center and part name for nicer look in GUI
    name = name.replace("Right", "Right ").replace("Left", "Left ").replace("Center", "Center ");
    ui->groupBox->setTitle(name.toUpper());

    //set size of inventory
    ui->inventory->setSlotsCount(locationData.inventorySlotsCount);

    //remember number of hardpoints
    hardpointsCount[HardpointTypes::Ballistic] = locationData.ballisticHardpoints;
    hardpointsCount[HardpointTypes::Missile] = locationData.missileHardpoints;
    hardpointsCount[HardpointTypes::Energy] = locationData.energyHardpoints;
    hardpointsCount[HardpointTypes::Antipersonnel] = locationData.supportHardpoints;

    auto setupHardpoint = [](int count, QLabel *left, QLabel *icon, QLabel *max)->void
    {
        if (count == 0)
        {
            left->hide();
            max->hide();
            icon->hide();
        }
        else
        {
            QString n(QString::number(count));
            left->setText(n);
            max->setText('/' + n);
        }
    };

    //setup hardpoint-related GUI elements
    setupHardpoint(locationData.ballisticHardpoints, ui->labelBallisticHardpointsLeft, ui->labelBallisticIcon, ui->labelMaxBallisticHardpoints);
    setupHardpoint(locationData.missileHardpoints, ui->labelMissileHardpointsLeft, ui->labelMissileIcon, ui->labelMaxMissileHardpoints);
    setupHardpoint(locationData.energyHardpoints, ui->labelEnergyHardpointsLeft, ui->labelEnergyIcon, ui->labelMaxEnergyHardpoints);
    setupHardpoint(locationData.supportHardpoints, ui->labelSupportHardpointsLeft, ui->labelSupportIcon, ui->labelMaxSupportHardpoints);

    //setup inventory
    ui->inventory->setupInventory(locationData.inventory);
}

przedtem miałem 3 różne funkcje MechPartWidget::setName(), setSlotsCount() i właśnie setupHardpoints() które były wywoływane w konstruktorze MechDesignWidget, ale zrefaktorowałem to na obecną postać.

0

Na początek powiem, że nigdy "nie czułem" tego typu gier, więc sorry jeśli moje pytania wydadzą się głupie ;)
Rozumiem, że te ikonki są zależne od tego co znajduje się w liście poniżej?
Jednego nie rozumiem:

  • Right Arm ma jeden M Laser i ikonki celownika i wybuchu
  • Head ma jeden M Laser i jedną ikonkę celownika
  • Right Torso ma sześć M Laserów i tylko ikonkę wybuchu
    Od czego to zależy?
    Co się powinno wyświetlić jeżeli Right Arm miałby dwa M Lasery?

Na pierwszy rzut oka to inventoryWidget zamieniłbym na styl model/view.
Taki model(dziedziczący po QAbstractListModel) zaopatrzyłbym w funkcję typu

unordered_map<InventoryType,int> countPoints(MechPartType part_type)

a MechPartWidget delegowałby te zliczone 'punkty' do odpowiedniego widgeta zajmującego się wyświetlaniem tych punktów i ikonek.(coś na styl jak w moim poprzednim poście).

Nie wiem czy poprawnie zrozumiałem logikę działania tego co pokazałeś no i nie wiem czy takie zmiany w kodzie nie wymagałyby wywrócenia całej aplikacji do góry nogami. Ja się już ostro przestawiłem na model/view, więc innych propozycji nie mam - czyli raczej nie pomogę :P

0

Od czego to zależy - dane są zapisane w dwóch plikach JSON, MechsDatabase robi ich parsowanie, po czym wrzuca do mapy <QString wariant mecha, tutaj HBK-4P, MechData>
Teraz ikonki i ich ilości - na razie jest na wczesnym etapie, więc odwzorowania nie są jeszcze w pełni zrobione. Generalnie ikonka 3 pocisków to ballistic, ikonka promienia trafiającego w coś energy, rakiety to missile, a ten celownik to support. W tymże przykładzie mech HBK-4P ma w rękach po jednym zaczepie energy i support, w prawym torsie 6 energy, w głowie 1 support - i co więcej ich maksymalna liczba (oznaczana jako cyfra z / na lewo od ikonki) się nie zmieni. Gwoli ścisłości te zapisy powinny wyglądać tak - 0/1 1/1 0/6 1/1(head) itd bo zaczepy są zajęte przez broń, ale tej funkcjonalności jeszcze nie zaimplementowałem.

O ListView też myślałem, ale trochę za wiele kombinowania było z InventoryItemami które zajmowały kilka wierszy (a jest takich mnóstwo).

Generalnie ja tu pomocy nie potrzebuję, kod dałem tylko jako ilustrację, że moja niechęć do dodawania kolejnych klas jest w tym przypadku mocno uzasadniona. A i na temat rozdziału funkcjonalności na klasy, oraz czytelności kodu jak mniemam posiadam rozeznanie.

Choć jeśli miałbym coś zmieniać, to Twoja propozycja Bracie @tajny_agent aby zgrupować na jednym Widgecie labelMaxTYPEHardpoints, labelTYPEIcon oraz labelTYPEHardpointsLeft jest najsensowniejsza ze wszystkich jakie tutaj otrzymałem.

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