{"id":1843,"date":"2019-06-06T11:11:36","date_gmt":"2019-06-06T09:11:36","guid":{"rendered":"https:\/\/www.kompikownia.pl\/?p=1843"},"modified":"2021-03-01T19:07:46","modified_gmt":"2021-03-01T18:07:46","slug":"4-unikalne-sposoby-na-problemy-ze-zwracaniem-wartosci-w-c","status":"publish","type":"post","link":"https:\/\/www.kompikownia.pl\/index.php\/2019\/06\/06\/4-unikalne-sposoby-na-problemy-ze-zwracaniem-wartosci-w-c\/","title":{"rendered":"4 unikalne sposoby na problemy ze zwracaniem warto\u015bci w C++"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span>\n<p>Czy wydaje ci si\u0119, \u017ce wiesz ju\u017c wszystko o zwracaniu warto\u015bci z funkcji\/metody? Istnieje mn\u00f3stwo przypadk\u00f3w, w kt\u00f3rych trzeba nieco pomy\u015ble\u0107 zanim napiszemy sygnatur\u0119 funkcji. W dzisiejszym artykule poznamy cztery sposoby na zwr\u00f3cenie warto\u015bci z funkcji w pewnym kontrowersyjnym przypadku. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Zadanie<\/h2>\n\n\n\n<p>Wyobra\u017a sobie, \u017ce jeste\u015b bardzo zaradnym programist\u0105 i realizujesz zlecenie warte du\u017c\u0105 ilo\u015b\u0107 pieni\u0119dzy. S\u0105dzisz, \u017ce potrafisz rozwi\u0105za\u0107 absolutnie ka\u017cdy problem. Stoi przed tob\u0105 nadzwyczaj proste zadanie: pobierz z bazy danych informacje o pracowniku znajduj\u0105cym si\u0119 pod specyficznym ID. <\/p>\n\n\n\n<p>Pozornie nie kryje si\u0119 tutaj nic skomplikowanego ani podchwytliwego, prawda? Ale to tylko z\u0142udzenie. Przy realizacji tego prostego zadania czeka na nas ca\u0142a masa r\u00f3\u017cnorodnych pu\u0142apek. Musimy chocia\u017cby si\u0119 zastanowi\u0107, co zrobi\u0107 gdy nie znajdziemy pracownika o podanym ID. Jak rozwi\u0105za\u0107 ten problem? Co zwr\u00f3ci\u0107 w takim wypadku?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">I spos\u00f3b <em>Unix way<\/em><\/h3>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor1\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n\nusing std::string;\nusing std::vector;\nusing std::cout;\nusing std::endl;\nstruct Worker {\n    int ID;\n    string name;\n    string surname;\n};\nvector&lt;Worker&gt; workerList {{1,\"Jan\",\"Kowalski\"},{2,\"Adam\",\"Nowak\"},{3,\"Juliusz\", \"S\u0142owacki\"}}; \/\/ symulacja bazy danych\n\nWorker getWorkerByID(int ID) {\n    for(Worker worker : workerList) {\n        if(worker.ID==ID) return worker;\n    }\n    return Worker{-1,\"\",\"\"};\n}\nint main()\n{\n    Worker work1 = getWorkerByID(1);\n    Worker work2 = getWorkerByID(4);\n    if(work1.ID!=-1) cout&lt;&lt;\"Pracownik ID 1:\"&lt;&lt;work1.name&lt;&lt;\" \"&lt;&lt;work1.surname&lt;&lt;endl;\n    else cout&lt;&lt;\"Nie znaleziono pracownika o ID 1\"&lt;&lt;endl;\n    if(work2.ID!=-1) cout&lt;&lt;\"Pracownik ID 2:\"&lt;&lt;work2.name&lt;&lt;\" \"&lt;&lt;work2.surname&lt;&lt;endl;\n    else cout&lt;&lt;\"Nie znaleziono pracownika o ID 4\"&lt;&lt;endl;\n}\n\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor1Result\"><\/pre>\n<\/div>\n\n\n\n<p>To jest najprostsze rozwi\u0105zanie. W ten spos\u00f3b do tego problemu podesz\u0142oby 95% student\u00f3w. Kod realizuje stawiane przed nim zadanie. Funkcja zwr\u00f3ci nam zawsze pracownika o specyficznym ID, je\u015bli go znajdzie. <\/p>\n\n\n\n<p>Jednak\u017ce, co zrobi\u0107 w sytuacji, kiedy pracownika o podanym jako argument ID nie ma w bazie? Mo\u017cemy zwr\u00f3ci\u0107 obiekt Worker o specyficznych warto\u015bciach atrybut\u00f3w (tak jak w powy\u017cszym kodzie, linijka 19). Ale wymaga to nieintuicyjnego ifa po stronie osoby korzystaj\u0105cej z napisanej przez nas funkcji. Dlaczego nieintuicyjnego? Poniewa\u017c korzystaj\u0105cy z naszej funkcji musi wiedzie\u0107 lub domy\u015ble\u0107 si\u0119, co m\u00f3wi o tym, \u017ce zwr\u00f3cony obiekt nie jest tym czego poszukiwa\u0142. Czy ID=-1 mo\u017ce to sugerowa\u0107? A mo\u017ce puste pole imi\u0119\/nazwisko? Taki spos\u00f3b rozwi\u0105zania problemu rodzi wiele komplikacji. <\/p>\n\n\n\n<p>Nazwa\u0142em ten spos\u00f3b &#8222;Unix way&#8221; gdy\u017c jest on powszechnie wykorzystywany w API systemu operacyjnego Linux. Tam wi\u0119kszo\u015b\u0107 funkcji w przypadku niepowodzenia zwraca warto\u015b\u0107&nbsp;-1. Aby uzyska\u0107 dok\u0142adny kod b\u0142\u0119du, trzeba nierzadko korzysta\u0107 z dodatkowych funkcji lub odczytywa\u0107 dodatkowe zmienne (errno)). <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">II spos\u00f3b rozwi\u0105zania problemu &#8211; wska\u017aniki i null<\/h3>\n\n\n\n<p>Wska\u017aniki &#8211; brzmi bardzo nieprzyjemnie. Ale wbrew pozorom jest to pewne rozwi\u0105zanie. Spr\u00f3bujmy przerobi\u0107 funkcj\u0119, tak aby korzysta\u0142a z ich dobrodziejstwa. <\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor2\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n\nusing std::string;\nusing std::vector;\nusing std::cout;\nusing std::endl;\nstruct Worker {\n    int ID;\n    string name;\n    string surname;\n};\nvector&lt;Worker&gt; workerList {{1,\"Jan\",\"Kowalski\"},{2,\"Adam\",\"Nowak\"},{3,\"Juliusz\", \"S\u0142owacki\"}}; \/\/ symulacja bazy danych\n\nWorker* getWorkerByID(int ID) {\n    for(Worker worker : workerList) {\n        if(worker.ID==ID) return new Worker(worker);\n    }\n    return nullptr;\n}\nint main()\n{\n    Worker* work1 = getWorkerByID(1);\n    Worker* work2 = getWorkerByID(4);\n    if(work1!=nullptr) {\n        cout&lt;&lt;\"Pracownik ID 1:\"&lt;&lt;work1-&gt;name&lt;&lt;\" \"&lt;&lt;work1-&gt;surname&lt;&lt;endl;\n        delete work1;\n    }\n    else cout&lt;&lt;\"Nie znaleziono pracownika o ID 1\"&lt;&lt;endl;\n    if(work2!=nullptr) {\n        cout&lt;&lt;\"Pracownik ID 2:\"&lt;&lt;work2-&gt;name&lt;&lt;\" \"&lt;&lt;work2-&gt;surname&lt;&lt;endl;\n        delete work2;\n    }\n    else cout&lt;&lt;\"Nie znaleziono pracownika o ID 4\"&lt;&lt;endl;\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor2Result\"><\/pre>\n<\/div>\n\n\n\n<p>Dla programisty korzystaj\u0105cego z naszej funkcji jej dzia\u0142anie wydaje si\u0119&nbsp;by\u0107 bardziej oczywiste ni\u017c poprzednio. Je\u015bli pracownik nie zosta\u0142 znaleziony &#8211; funkcja zwraca null. Problemem jest zarz\u0105dzanie pami\u0119ci\u0105. Korzystaj\u0105cy z naszej funkcji musi pami\u0119ta\u0107 o zwolnieniu pami\u0119ci. Mogliby\u015bmy oczywi\u015bcie skorzysta\u0107 z dobrodziejstwa nowoczesnego wska\u017anika <a href=\"https:\/\/www.kompikownia.pl\/index.php\/2018\/09\/22\/inteligentne-wskazniki-unique_ptr\/\">unique_ptr<\/a> czy <a href=\"https:\/\/www.kompikownia.pl\/index.php\/2018\/09\/25\/shared_ptr-inteligetne-wskazniki\/\">shared_ptr<\/a>. Pomin\u0119li\u015bmy to w tym przyk\u0142adzie, gdy\u017c zaciemni\u0142o by kod i nie pozwoli\u0142o si\u0119 skupi\u0107 na g\u0142\u00f3wnym przekazie. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">III spos\u00f3b rozwi\u0105zania problemu &#8211; wyj\u0105tki<\/h3>\n\n\n\n<p>A co gdyby\u015bmy zatrudnili do rozwi\u0105zania naszej bol\u0105czki wyj\u0105tki? W Javie s\u0105&nbsp;one u\u017cywane do obs\u0142ugi praktycznie ka\u017cdej sytuacji wyj\u0105tkowej. Dlaczego nie mieliby\u015bmy zrobi\u0107 czego\u015b takiego samego w C++?<\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor3\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n\nusing std::string;\nusing std::vector;\nusing std::cout;\nusing std::endl;\nstruct Worker {\n    int ID;\n    string name;\n    string surname;\n};\nvector&lt;Worker&gt; workerList {{1,\"Jan\",\"Kowalski\"},{2,\"Adam\",\"Nowak\"},{3,\"Juliusz\", \"S\u0142owacki\"}}; \/\/ symulacja bazy danych\nclass NotFoundException : public std::exception {\npublic:\n    virtual char const * what() const noexcept override{return \"Nie znaleziono obiektu\";}\n};\nWorker getWorkerByID(int ID) {\n    for(Worker worker : workerList) {\n        if(worker.ID==ID) return worker;\n    }\n    throw NotFoundException();\n}\nint main()\n{\n    try {\n        Worker work1 = getWorkerByID(1);\n        cout&lt;&lt;\"Pracownik ID 1:\"&lt;&lt;work1.name&lt;&lt;\" \"&lt;&lt;work1.surname&lt;&lt;endl;\n    }\n    catch(NotFoundException e) {\n        cout&lt;&lt;e.what();\n    }\n    try {\n        Worker work2 = getWorkerByID(4);\n        cout&lt;&lt;\"Pracownik ID 4:\"&lt;&lt;work2.name&lt;&lt;\" \"&lt;&lt;work2.surname&lt;&lt;endl;\n    }\n    catch(NotFoundException e) {\n        cout&lt;&lt;e.what();\n    }\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor3Result\"><\/pre>\n<\/div>\n\n\n\n<p>Rozwi\u0105zanie jest &#8222;prawie&#8221; idealne i na pewno jest o wiele lepsze od poprzedniego sposobu. <\/p>\n\n\n\n<p class=\"example_code\">Zrobili\u015bmy sobie wyj\u0105tek <em>NotFoundException<\/em>. Wyrzucamy go wtedy, gdy pracownik o danym w sygnaturze funkcji ID nie zostanie odnaleziony.  Od funkcji dostajemy zwyk\u0142\u0105 zmienn\u0105, kt\u00f3rej czasem \u017cycia zarz\u0105dza kompilator. Nie musimy zatem dba\u0107 o zarz\u0105dzanie pami\u0119ci\u0105. Ponadto ka\u017cda z sytuacji &#8211; powodzenie i pora\u017cka &#8211; s\u0105 jasno rozr\u00f3\u017cnialne i oznajmiane albo zwr\u00f3ceniem odpowiedniej warto\u015bci albo wyrzuceniem wyj\u0105tku. Jaka jest wada? Dobrze zrealizowan\u0105 obs\u0142ug\u0119 wyj\u0105tk\u00f3w w C++ <a href=\"https:\/\/blog.plan99.net\/what-s-wrong-with-exceptions-nothing-cee2ed0616\">ci\u0119\u017cko zaprojektowa\u0107 i zakodowa\u0107.<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Najlepszy spos\u00f3b &#8211; optional <\/h2>\n\n\n\n<p class=\"example_code\"><em>optional<\/em> jest nowym typem wprowadzonym wraz ze standardem C++17. Czym wyj\u0105tkowym si\u0119 charakteryzuje? Optional jest zwyk\u0142\u0105 klas\u0105, kt\u00f3rej obiekt mo\u017ce przechowywa\u0107 dowolny inny obiekt lub warto\u015b\u0107. Ponadto <em>optional<\/em> wie o tym, czy jest wype\u0142niony jak\u0105\u015b zawarto\u015bci\u0105&nbsp;czy jest pusty. Dzi\u0119ki temu mo\u017cemy \u0142atwo sprawdzi\u0107, czy funkcja zwr\u00f3ci\u0142a rzeczywi\u015bcie jakie\u015b dane czy nie zwr\u00f3ci\u0142a nic. Wystarczy zapyta\u0107 <em>optionala<\/em>: &#8222;Czy jeste\u015b wype\u0142niony jak\u0105\u015b zawarto\u015bci\u0105&#8221;? On nam uprzejmie odpowie, dzi\u0119ki czemu nasz kod b\u0119dzie bardziej elegancki.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Jak u\u017cy\u0107 std::optional?<\/h3>\n\n\n\n<p class=\"example_code\">Je\u015bli nasz kompilator wspiera standard C++17, dodajemy nag\u0142\u00f3wek <em>&lt;optional&gt;<\/em>. Optional znajduje si\u0119 wtedy w przestrzeni nazw <em>std<\/em>. Je\u015bli posiadamy starsz\u0105 wersj\u0119 kompilatora gcc (np.: t\u0105 dostarczan\u0105 z wersj\u0105 C::B 17.12) powinni\u015bmy do\u0142\u0105czy\u0107 nag\u0142\u00f3wek <em>&lt;experimental\/optional&gt;<\/em> Optional znajduje si\u0119 wtedy w przestrzeni nazw <em>std::experimental. <\/em><\/p>\n\n\n\n<p>Przer\u00f3bmy analizowany wcze\u015bniej przyk\u0142ad tak, aby korzysta\u0142 z optionali. <\/p>\n\n\n\n<div class=\"cppCompiler\">\n<div class=\"cppPoweredBy\">\nPowered By <a target=\"_blank\" href=\"https:\/\/coliru.stacked-crooked.com\/\" rel=\"noopener noreferrer\">Coliru Online Compiler<\/a> and <a target=\"_blank\" href=\"https:\/\/ace.c9.io\/\" rel=\"noopener noreferrer\">Ace editor<\/a>\n<\/div>\n<div class=\"cppEditorParent\">\n<pre class=\"cppEditor\" id=\"editor0\" style=\"width: 500px\">#include &lt;iostream&gt;\n#include &lt;vector&gt;\n#include &lt;experimental\/optional&gt;\n\nusing std::string;\nusing std::vector;\nusing std::cout;\nusing std::endl;\nusing std::experimental::optional;\nstruct Worker {\n    int ID;\n    string name;\n    string surname;\n};\nvector&lt;Worker&gt; workerList {{1,\"Jan\",\"Kowalski\"},{2,\"Adam\",\"Nowak\"},{3,\"Juliusz\", \"S\u0142owacki\"}}; \/\/ symulacja bazy danych\n\noptional&lt;Worker&gt; getWorkerByID(int ID) {\n    optional&lt;Worker&gt; opt;\n    for(Worker worker : workerList) {\n        if(worker.ID==ID) opt = worker;\n    }\n    return opt;\n}\nint main()\n{\n    optional&lt;Worker&gt; work1 = getWorkerByID(1);\n    optional&lt;Worker&gt; work2 = getWorkerByID(4);\n    if(work1) cout&lt;&lt;\"Pracownik ID 1:\"&lt;&lt;work1-&gt;name&lt;&lt;\" \"&lt;&lt;work1-&gt;surname&lt;&lt;endl;\n    else cout&lt;&lt;\"Nie znaleziono pracownika o ID 1\"&lt;&lt;endl;\n    if(work2) cout&lt;&lt;\"Pracownik ID 4:\"&lt;&lt;work2-&gt;name&lt;&lt;\" \"&lt;&lt;work2-&gt;surname&lt;&lt;endl;\n    else cout&lt;&lt;\"Nie znaleziono pracownika o ID 4\"&lt;&lt;endl;\n}\n<\/pre>\n\n<\/div>\n<pre class=\"EditorResult\" id=\"editor0Result\"><\/pre>\n<\/div>\n\n\n\n<p class=\"example_code\">Zauwa\u017camy zmian\u0119 definicji funkcji w linijce 17. Teraz funkcja zwraca obiekt <em>optional<\/em> przechowuj\u0105cy obiekty klasy Worker. Pustego optionala tworzymy w linijce 18. Jak wpisujemy co\u015b do optionala? Po prostu stosujemy zwyk\u0142e przypisanie. Przyk\u0142ad znajduje si\u0119 w linijce 20. <\/p>\n\n\n\n<p class=\"example_code\">Jak sprawdzamy czy optional przechowuje jak\u0105\u015b warto\u015b\u0107? Po prostu weryfikujemy, czy obiekt zwraca true czy false. Optional posiada przeci\u0105\u017cony operator bool, dzi\u0119ki temu taki mechanizm dzia\u0142ania jest mo\u017cliwy. <\/p>\n\n\n\n<p class=\"example_code\">Je\u015bli chcesz pozna\u0107 bardziej zaawansowane cechy <em>Optionala<\/em>, <a href=\"https:\/\/cpp-polska.pl\/post\/jak-uzywac-stdoptional-z-c17\">w tym miejscu jest to ciekawie opisane<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Podsumowuj\u0105c<\/h2>\n\n\n\n<p class=\"example_code\">Ten artyku\u0142 mia\u0142 za zadanie w kr\u00f3tki spos\u00f3b przedstawi\u0107 ci przegl\u0105d rozwi\u0105za\u0144 problemu. Problemu polegaj\u0105cego na obs\u0142udze sytuacji wyj\u0105tkowych podczas zwracania warto\u015bci. <em>Optional <\/em>jest najlepszym mo\u017cliwym rozwi\u0105zaniem w sytuacji, kiedy chcemy zwr\u00f3ci\u0107 jaki\u015b obiekt lecz istniej\u0105 sytuacje kiedy ten obiekt nie b\u0119dzie istnia\u0142. Innym przydatnym sposobem kt\u00f3ry warto zna\u0107 s\u0105 wyj\u0105tki. Te dwie metody s\u0105 dwoma najcz\u0119\u015bciej obecnie u\u017cywanymi. Najgorszym sposobem jest pierwszy. Powinni\u015bmy go u\u017cywa\u0107 tylko w sytuacji, kiedy absolutnie nie mamy innego wyboru. <\/p>\n\n\n\n<p>Dzi\u0119ki za lektur\u0119 i powodzenia \ud83d\ude42 Je\u015bli masz jakie\u015b w\u0105tpliwo\u015bci, uwagi, podziel si\u0119 swoj\u0105 opini\u0105 \ud83d\ude42 <\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Czas czytania:<\/span> <span class=\"rt-time\">4<\/span> <span class=\"rt-label rt-postfix\">minut<\/span><\/span> Czy wydaje ci si\u0119, \u017ce wiesz ju\u017c wszystko o zwracaniu warto\u015bci z funkcji\/metody? Istnieje mn\u00f3stwo przypadk\u00f3w, w kt\u00f3rych trzeba nieco pomy\u015ble\u0107 zanim napiszemy sygnatur\u0119 funkcji. W dzisiejszym artykule poznamy cztery &#8230;<\/p>\n","protected":false},"author":1,"featured_media":462,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[31],"tags":[],"_links":{"self":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/1843"}],"collection":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/comments?post=1843"}],"version-history":[{"count":68,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/1843\/revisions"}],"predecessor-version":[{"id":2563,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/posts\/1843\/revisions\/2563"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media\/462"}],"wp:attachment":[{"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/media?parent=1843"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/categories?post=1843"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kompikownia.pl\/index.php\/wp-json\/wp\/v2\/tags?post=1843"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}