Czy dziedziczenie struktur jest poprawne?

0

Chciałem zrobić serializacje struktur które dziedziczą po sobie struct personEXT :public person
Klasa która dziedziczy wykorzystuje funkcje serializacji to_json/from_json przodka

Działa , ale czy to jest zgodne ze sztuką ?
Czy można to zrobić bez operatora personEXT& operator=(person const& copy) ?

#include <iostream>
#include <string>

#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>

// for convenience
using json = nlohmann::json;

namespace ns {
    // a simple struct to model a person
    struct person {
        std::string name;
        std::string address;
        int age;
    };

    void to_json(json& j, const person& p) {
        j = json{{"name", p.name},
                 {"address", p.address},
                 {"age", p.age}};
    }
    void from_json(const json& j, person& p) {
        j.at("name").get_to(p.name);
        j.at("address").get_to(p.address);
        j.at("age").get_to(p.age);
    }

    struct personEXT :public person
    {
       std::string surname;
       personEXT& operator=(person const& copy)
       {
        person::operator=(copy); // 
        return *this;
       };
    };

    void to_json(json& j, const personEXT& p) {
        j = static_cast<person>(p);
        j["surname"]=p.surname;
    }

    void from_json(const json& j, personEXT& p) {
        p = j.get<person>();
        j.at("surname").get_to(p.surname);
    }
}


int main()
{
    ns::personEXT pEXT{"nameEXT", "adressEXT",99,"surname"};
    json jEXT =pEXT;
    fmt::print("\n\n{}",jEXT.dump(2));

    ns::personEXT pETX_new = jEXT.get<ns::personEXT>();
    json jEXT_new =pETX_new;
    fmt::print("\n\n{}",jEXT_new.dump(2));
}

https://godbolt.org/z/EzGKaPcEP

0

@Adamek Adam:

Czy C++ jest właściwym językiem do dżejsnowania ...
Wszystko się da, nawet hełm na lewą stroną założyć w przypływie paniki, ale wszystko ma swoje koszty. ze 2-5x wiecej linii kodu adaptujacego do JSONa niż np definicja struktury, który ktoś musi konserwować i to po strinagach
W Javie / C# (językach z refleksją) to kosztuje ZERO

1

@ZrobieDobrze drzewo interpretacji Twojej odpowiedzi jest dość enigmatyczne i nie wiem jak zinterpretować to co napisałeś , chodzi o to aby nie urzywać C++ czy nie używać JSON, czy też nie łączyć jednego z drugim ?

0

W postach pytasz o pewien "pakiet" soluszynów C++, z którego nie umiem zgadnąć rodzaju projektu, ale wspólnym mianownikiem jest "dynamiczność"
Hipotetyczne "gdybym ja to robił", co mam świadomosc w gadaniu jest łatwe, ale bym wyniósł raz na zawsze encje biznesowe do świata dynamicznego, zachowując silnik tegoż w C++ *)

Tą sama encję (person) wcześniej czy pózniej trzeba będzie zapisać/odczytać z/do bazy, przybindowac do GUI, wypchnąć na papier. Myśląc o encji, że implementuje klasę integrującą wg wymogów libki Jsonwksiej, to rozwiązujesz jeden problem, generując następne.

*) o ile pozostając w C++ ...

2

Patrząc na przykłady z GH issues https://github.com/nlohmann/json/issues/2199 to nie powinieneś potrzebować jawnie zdeklarowanego operatora.

0

Dziękuje !

Na to rozwiązanie nie wpadłem:

    void from_json(const json& j, personEXT& p) {
        nlohmann::from_json(j, static_cast<person&>(p));
        j.at("surname").get_to(p.surname);
    }

I na koniec całe rozwiązanie
https://godbolt.org/z/Ej73rz6zc

2

Dziedziczenie wydaje mi się być słabym rozwiązaniem. Lepiej trzymać person jako pole a w klasie personEXT możesz zadecydować czy część z person będzie na tym samym poziomie czy może poziom niżej tj. {"surname": "Foo", "person":{}}

1
slsy napisał(a):

Dziedziczenie wydaje mi się być słabym rozwiązaniem. Lepiej trzymać person jako pole a w klasie personEXT możesz zadecydować czy część z person będzie na tym samym poziomie czy może poziom niżej tj. {"surname": "Foo", "person":{}}

To też miałem powyzej na myśli, może nie idealnie wyraziłem. Takie dziedziczenie "usztywnia" i ma cechy szkolnego przykłądu negatrywnego "jak nie dziedziczyć"
złe jest class Person : BeReadyForJson {}

Znacznie lepsze
class PersonJsonWrapper { Person & }

a mi bardziej po łbie (zupełnie z czapy z myślą o weekendzie):

class Person : BeReadyForDynamicAccess { }

class <T /*Person*/ > ForJsonWrapper { ForJsonWrapper(BeReadyForDynamicAccess & ) }

class <T> ForQtWrapper { ForQtWrapper(BeReadyForDynamicAccess & ) }

class <T> ForPrintingWrapper { ForPrintingWrapper(BeReadyForDynamicAccess & ) }

class <T> ForSqlWrapper { ForSqlWrapper(BeReadyForDynamicAccess & ) }

5

Działa , ale czy to jest zgodne ze sztuką

W C++ typ zdefiniowany jako struct różni się od class tylko tym, że w class elementy są domyślnie private, a w struct są domyślnie public.

Czyli zamiast

struct Foo {
   ...
};

mógłbyś dać

class Foo {
  public:
    ...
};

, a zamiast

class Bar {
  ...
};

mógłbyś zrobić

struct Bar {
  private:
    ...
};

.

Wszystkie konstrukcje składniowe działające w obrębie class działają też tak samo w struct.

0

Inspiracją do tego wątku była pierwsza odpowiedz w Przygotowanie do rozmowy technicznej z Javy
top 100 java interview questions
wpisałem w wyszukiwarkę top 100 c++ interview questions
Pojawiło sie pytanie: Q-8. What is the difference between struct and class?
Odpowiedz: Structure does not support inheritance.
Stwierdziłem na przykładzie (zgodnie z dotychczasową wiedza) że to nie prawda i nic sie nie zmieniło a że akurat przed sekundą używałem nlohmann::json to zadałem sobie pytanie jak to zrobić dla powstałego przykładu.

@ZrobieDobrze nie rozumiem co masz na myśli class <T> ForPrintingWrapper { ForPrintingWrapper(BeReadyForDynamicAccess & ) }
musiałby być trochę bogatszy przykład, czym jest BeReadyForDynamicAccess ?

0
Adamek Adam napisał(a):

@ZrobieDobrze nie rozumiem co masz na myśli class <T> ForPrintingWrapper { ForPrintingWrapper(BeReadyForDynamicAccess & ) }
musiałby być trochę bogatszy przykład, czym jest BeReadyForDynamicAccess ?

Krótko: dorobieniem refleksji, która by była w Javie i C# (Pythonie, Lua, JS, itd). Zdolność klasy / struktury C++ aby znała po stringach swoje pola i umiała set/get po stringu zrealizować

Libki, którą przywołałeś w pierwszym poście nie znam, ale wzorzec tablica[string, getter, setter] pojawia się zawsze, gdy interfejsujemy wbudowany (embedded) interpreter (Lua, Python - robiłem i to na kilka sposobów) z hostem w C/C++
(interfejsując interpreter eksportujemy tez po stringowej nazwie zaplanowane metody (jakby publiczne), czyli tam jest więcej, JSON ma łatwiej bo tylko pola/property )

Lua_bind czy to:
https://www.boost.org/doc/libs/1_79_0/libs/parameter_python/doc/html/index.html
(to skrajnie intelektualne - Luabind jest mocno prostsze. Eksporty do C jeszcze prostsze, ale wiecej głupawego kodu)

1

Ile razy próbowałem dziedziczyć po strukturach, tyle razy prędzej, czy później przywalałem w ścianę i czegoś tam nie dało się dalej robić. Enkapsulacja Person w PersonExt jest dużo bardziej elastyczna i daje mniejsze szanse na wpakowanie się w kanał. Prędzej czy później pojawia się problem, po której strukturze odziedziczyć kolejną strukturę i musisz wybierać, czy ma to być OsobaZNazwiskiemStanowiskiemIDatąUrodzenia, czy UżytkownikSerwisuZEmailemIPodłączonymFacebookiem.

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