3. týden týden týden týden týden týden Termín DÚ: vždy úterý 9:00. Zadání DÚ 1

Podobné dokumenty
bonus (max +2 body): bezpečné řešení v debug módu a[x][y] (a.rows()[x][y] a.cols()[x][y]) odchytávat všechny nedefinované akce

C++ a vnitřní svět. David Bednárek Jakub Yaghob Filip Zavoral

David Bednárek Jakub Yaghob Filip Zavoral.

Cvičení z programování v C++ ZS 2016/2017 Přemysl Čech

Úvod do jazyka C. Ing. Jan Fikejz (KST, FEI) Fakulta elektrotechniky a informatiky Katedra softwarových technologií

PB161 Programování v jazyce C++ Přednáška 10

PB161 Programování v jazyce C++ Přednáška 10

Šablonové metaprogramování v C++ Miroslav Virius KSI FJFI ČVUT

Obsah. Předmluva 13 Zpětná vazba od čtenářů 14 Zdrojové kódy ke knize 15 Errata 15

Obsah přednášky. 12. Dokumentace zdrojového kódu Tvorba elektronické dokumentace UML. Co je diagram tříd. Ing. Ondřej Guth

PB přednáška (23. listopadu 2015)

termín: utorok :00 (poledne) Cíle: Zadání:

Domácí úkoly 2013/14

Základy programování (IZP)

Jazyk C++ II. STL knihovna kontejnery část 1

PB161 Programování v jazyce C++ Přednáška 9

Jazyk C# (seminář 6)

IUJCE 07/08 Přednáška č. 1

Jazyk C++ I. Šablony 2

PROGRAMOVÁNÍ V C++ CVIČENÍ

Zápis programu v jazyce C#

Jazyk C++ I. Šablony 3

Pokročilé programování v jazyce C pro chemiky (C3220) Statické proměnné a metody, šablony v C++

Práce s binárními soubory. Základy programování 2 Tomáš Kühr

3. týden Kontejner. 5. týden Exception safe policy. 7. týden 7.4. SAX C opendir. 9. týden Multithreaded network

Programovací jazyk C++ Hodina 1

Operační systémy. Cvičení 3: Programování v C pod Unixem

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

PB161 Programování v C++ Proudy pro standardní zařízení Souborové proudy Paměťové proudy Manipulátory

Základy programování (IZP)

Algoritmizace a programování

PB161 Programování v jazyce C++ Přednáška 8

Jazyk C++ II. Šablony a implementace

NPRG051 Pokročilé programování v C /17 Úkol 2

Jazyk C++ I. Šablony

Úvod do programovacích jazyků (Java)

map, multimap - Asociativní pole v C++.

Preprocesor a koncepce (větších) programů. Úvod do programování 2 Tomáš Kühr

<surface name="pozadi" file="obrazky/pozadi/pozadi.png"/> ****************************************************************************

Martin Flusser. Faculty of Nuclear Sciences and Physical Engineering Czech Technical University in Prague. December 7, 2016

Michal Krátký. Úvod do programovacích jazyků (Java), 2006/2007

Standardní algoritmy vyhledávací.

Základy programování (IZP)

Základy jazyka C# Obsah přednášky. Architektura.NET Historie Vlastnosti jazyka C# Datové typy Příkazy Prostory jmen Třídy, rozhraní

Práce se soubory. Úvod do programování 2 Tomáš Kühr

Data, výrazy, příkazy

Programovací jazyk C(++) C++ area->vm_mm->locked_vm -= len >> PAGE_SHIFT;

Abstraktní datové typy

Množina v C++ (set, multiset).

Výrazy, operace, příkazy

Platforma.NET 11.NET Framework 11 Visual Basic.NET Základní principy a syntaxe 13

PODOBÁ SE JAZYKU C S NĚKTERÝMI OMEZENÍMI GLOBÁLNÍ PROMĚNNÉ. NSWI162: Sémantika programů 2

Paměť počítače. alg2 1

VISUAL BASIC. Práce se soubory

Struktury a dynamická paměť

Domácí úkoly Pokročilé programování v C++ LS 2015/2016. NPRG051 Pokročilé programování v C / DÚ

Obsah. Úvodem 15. Zaměření knihy 15 Co v knize najdete 15 Doprovodné CD 17 Poděkování 18

DSL manuál. Ing. Jan Hranáč. 27. října V této kapitole je stručný průvodce k tvorbě v systému DrdSim a (v

Úvod do programovacích jazyků (Java)

PROMĚNNÉ, KONSTANTY A DATOVÉ TYPY TEORIE DATUM VYTVOŘENÍ: KLÍČOVÁ AKTIVITA: 02 PROGRAMOVÁNÍ 2. ROČNÍK (PRG2) HODINOVÁ DOTACE: 1

Konec a tak. PB173 Programování v C++11. Vladimír Štill, Jiří Weiser. Fakulta Informatiky, Masarykova Univerzita. 15.

Objektově orientované programování

Vstupní a vstupní proudy v C++

Řídicí struktury. alg3 1

Funkční objekty v C++.

Opakování programování

Programování v jazyce C a C++

8 Třídy, objekty, metody, předávání argumentů metod

Java Cvičení 01. CHARLES UNIVERSITY IN PRAGUE faculty of mathematics and physics

Cvičení č. 2. Komunikace mezi procesy Program Hodiny. 4 body

Algoritmizace a programování

Programovací jazyk Java

1. lekce. do souboru main.c uložíme následující kód a pomocí F9 ho zkompilujeme a spustíme:

Šablony, kontejnery a iterátory

Programování v Javě I. Leden 2008

Webový server lapache

Školská sieť EDU. Rozdelenie škôl. Obsah: Deleba škôl podľa času zaradenia do projektu: Delba škôl podľa rýchlosti pripojenia:

C++ 0x aka C++11. Základním kamenem je třída std::thread

IUJCE Přednáška č. 11. další prvky globální proměnné, řízení viditelnosti proměnných, funkcí

Dynamika objektů. Karel Richta a kol. katedra počítačů FEL ČVUT v Praze

PROGRAMOVACÍ JAZYKY A PŘEKLADAČE LEXIKÁLNÍ ANALÝZA

Příkazy preprocesoru - Před překladem kódu překladačem mu předpřipraví kód preprocesor - Preprocesor vypouští nadbytečné (prázdné) mezery a řádky -

Šablony, kontejnery a iterátory

TG Motion verze 4 Modul Virtuální PLC návod k obsluze

Univerzita Palackého v Olomouci Radek Janoštík (Univerzita Palackého v Olomouci) Základy programování 4 - C# 19.2.

Bridge. Známý jako. Účel. Použitelnost. Handle/Body

18. února 2015, Brno Připravil: David Procházka. Programovací jazyk C++

Rozšíření ksh vůči sh při práci s proměnnými

Programování v Javě I. Únor 2009

přetížení operátorů (o)

Vytváření a použití knihoven tříd

2 PŘÍKLAD IMPORTU ZATÍŽENÍ Z XML

TG Motion verze 4 Modul Virtuální PLC návod k obsluze

Výrazy, operace, příkazy

Algoritmizace a programování

PB161 Programování v jazyce C++ Přednáška 3

Práce se soubory. Základy programování 2 Tomáš Kühr

2 Základní funkce a operátory V této kapitole se seznámíme s použitím funkce printf, probereme základní operátory a uvedeme nejdůležitější funkce.

2) Napište algoritmus pro vložení položky na konec dvousměrného seznamu. 3) Napište algoritmus pro vyhledání položky v binárním stromu.

přetížení operátorů (o)

Transkript:

3. týden 6.3. Zadání DÚ 1 5. týden 20.3. Termín DÚ 1, Zadání DÚ 2 7. týden 3.4. Termín DÚ 2, Řešení DÚ 1, Zadání DÚ 3 9. týden 17.4. Termín DÚ 3, Řešení DÚ 2, Zadání DÚ 4 11. týden 1.5. Termín DÚ 4 13. týden 22.5. Řešení DÚ 3, Řešení DÚ 4 Termín DÚ: vždy úterý 9:00

termín: středa 23.3. 12:00 (poledne) Cíle: Multiplatformnost Návrh rozhraní Externí knihovny Asynchronní čtení souborů Zadání: Vytvořte objektovou knihovnu pro multiplatformní zastřešení platformně závislých knihoven pro asynchronní přístup k souborům. Knihovna musí zahrnovat alespoň POSIX AIO a Windows Asynchronous I/O.

#ifdef _MSC_VER... zavisly kod Windows #elif GNUG... zavisly kod POSIX #endif HANDLE f = CreateFile( f.txt, FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS FILE_FLAG_OVERLAPPED, 0); OVERLAPPED o; memset(&o, 0, sizeof(overlapped)); o.hevent = CreateEvent(0, TRUE, FALSE, 0); o.offset = offs; ReadFile(f, buf, sz, &rsz, &o); nejaky_uzitecny_kod WaitForSingleObject(o.hEvent, INFINITE); CloseHandle(o.hEvent); CloseHandle(f);

int f = open( f.txt, O_RDONLY O_LARGEFILE, rights); aiocb a; memset(&a, 0, sizeof(aiocb)); a.aio_fildes = f; a.aio_offset = offs; a.aio_nbytes = sz; a.aio_buf = buf; a.aio_sigevent.sigev_notify = SIGEV_NONE; aio_read(&a); nejaky_uzitecny_kod if( aio_error( &a)!= EINPROGRESS) yyy // finished aio_suspend(&a, 1, 0); rsz = aio_return(&a); close(f);

Otevření souboru Začátek čtení Dotaz na připravenost výsledku Čekání na výsledek Na otevřeném souboru lze opakovat čtení Po uzavření souboru lze objekt znovu použít otevřením jiného souboru Operace v nevhodných stavech (např. čtení neotevřeného souboru) musí vyvolat výjimku Jakékoliv chybové nebo výjimečné stavy musí být zpracovány vyvoláním výjimky

Otevři soubor f.txt Začni číst z xx bajtů z offsetu yy do bufferu zz Dělej něco užitečného Občas se zeptej, jestli už je dočteno Počkej na výsledek Bonusy - funkčnost navíc za extra body: Rozhraní nenutící uživatele k používání sizeof() nebo dynamic_cast při použití strukturovaných bufferů Čekání na libovolný počet událostí

Jednotné OO rozhraní nevázané na konkrétní platformu Čistá a hezká syntaxe i sémantika Typy parametrů a návratových hodnot Ošetření výjimečných a chybových stavů Korektní stavové chování Bezwarningová zkompilovatelnost Včastnost odevzdání Omezení : Jen read Čekání jen na jednu událost Není nutné propagovat platformově závislé chybové kódy stačí vyvolání výjimky Není nutné podporovat různé způsoby otevření, práva, bitíky apod. stačí nejjednodušší otevřít soubor pro čtení (binárně) Není nutné ošetřovat jiné způsoby přerušení čtení (signály)

Pouze přes Grupík Vše v jednom.zip souboru do sloupečku DU1 Názvy zdrojáků (povinně): du1_aio.h du1_aio.cpp du1_main.cpp Neposílejte žádné makefile,.prj,.sln,.obj,.exe,.pdb a jakékoliv další vedlejší soubory za poslání nevhodného obsahu budou strhnuty body na začátek každého souboru vložte komentář typu // DU1-AIO.cpp // Karel Vomacka NPRG051 2010/2011

termín: středa 6.4. 12:00 (poledne) Cíle: Pokročilé použití šablon Politiky Zadání: Vytvořte objektovou knihovnu pro parsování argumentů předaných programu na příkazové řádce. Knihovna musí umožnit uživateli pomocí politik nastavit její detailní chování.

Podpora přepínačů bez parametrů tail --help Podpora přepínačů s typovaným parametrem (řetězce, číslo,...) make f makefile j 20 Podpora typovaných argumentů (řetězce, čísla,...) seq 10 20 1 Snadná rozšiřitelnost množiny typů, které knihovna umí naparsovat Bonus formátovaný výpis nápovědy programu

Konvence DOS/Windows dir /L /A:D POSIX ls s width=40 T 8 Java java cp. verbose:jni ds Co dělat při neznámém přepínači Vyhodit výjimku Ignorovat Považovat za argument Vypsat chybu a skončit

Co dělat s rozpoznanými přepínači Nechat být Odstranit z argv a aktualizovat argc Použití u různých knihoven, viz gtk_init, QApplication,... Co dělat při chybném formátu parametru přepínače nebo argumentu Vyhodit výjimku Vypsat chybu a skončit Co dělat s neočekávanými argumenty Ignorovat Vyhodit výjimku Vypsat chybu a skončit Parser musí umět pracovat s ANSI znaky (char) i s širokými znaky (wchar_t)

std::wstring file_name; size_t count; size_t size; bool verbose; std::vector<std::wstring> argumenty; inicializace knihovny pro konvenci POSIX s wchar_t - hodnotu parametru přepínače f nebo --file ulož do file_name - hodnotu parametru přepínače c nebo --count ulož do size - hodnotu parametru přepínače --size ulož do size - pokud se vyskytne přepínač v nebo -verbose ulož do verbose true - argumenty přidej do vektoru argumenty spusť parsování Příklad je pro konvenci POSIX, pro ostatní konvecne by mělo být použití obdobné. Kromě vektorů by mělo být možné vkládat argumenty do listů, množin apod.

enum prefix_type { KILO, MEGA, GIGA }; prefix_type prefix; inicializace knihovny pro konvenci POSIX - hodnotu parametru přepínače p nebo -prefix ulož do prefix spusť parsování Pozn.: hodnota parametru přepínače p resp. -prefix může být jedno z písmen K (KILO), M (MEGA) nebo G (GIGA). Je tedy nutné zajistit, aby se textová hodnota parametru přepínače převedla na správnou hodnotu výčtu. Funkci, která převede text na hodnotu daného typu musí samozřejmě napsat uživatel knihovny. Knihovna by ale měla poskytovat snadno použitelný mechanismus, jak integrovat uživatelské konvertory.

std::string log_file; inicializace knihovny pro konveci POSIX tak, aby rozpoznané přepínače odstraňovala z argv a argc a ignorovala nerozpoznané přepínače - hodnotu parametru přepínače l nebo --log ulož do log_file spusť parsování Po spuštění programu s parametry: -f soubor1 l soubor2 soubor3 a spuštění parsování, bude argc a argv stejné, jako kdyby byl program spuštěn s parametry: -f soubor1 soubor3

bool show_help; enum attribute_type { HIDDEN, DIRECTORY }; attribute_type attribute; inicializace knihovny pro konvenci DOS - pokud se vyskytne přepínač /H ulož do show_help true, nápověda k přepínači je Show help - hodnotu parametru přepínače /A ulož do attribute, nápověda k přepínači je File attribute. Supported types are:\nh Hidden\nD Directory zobraz nápovědu Výsledek: /H Show help /A:attribute File attribute. Supported types are: H - Hidden D - Directory Použití pro ostatní konvence by mělo být podobné a výstup by měl odpovídat zvolené konvenci.

Nezapomenout na možnost částečné nebo parciální specializace šablon, takže lze dosáhnout jiného chování pro různé typy, např. pro typ char a wchar_t. Prozkoumat, co je ve skutečnosti std::string, std::stream atd. a jestli toho nelze využít pro pohodlnou a jednotnou práci s různými typy znaků. Všechny kontejnery v STL umožňují zjistit typ prvků, které uchovávají pomocí členského typu value_type.

Kvůli různým typům znaku (char a wchar_t) budou pravděpodobně i samotné politiky šablonami. Pokud je potřeba specifikovat, že parametrem šablony je opět šablona, je třeba postupovat přibližně takhle: template <template <typename> class TPolicy, typename TChar> struct trida{ void do_something() { TPolicy<TChar>::do_something(); } }; template <typename TChar> struct politika { static void do_something() {} }; trida<politika, char> t; t.do_something();

Čistá a hezká syntaxe i sémantika Kvalita navrženého rozhraní knihovny Není třeba si lámat hlavu s tím, jak přesně se má knihovna chovat, jaké všechny formáty argumentů akceptovat a kolik mezer vypisovat. Zadání je schválně vágní, takže řešení mohou být značně variabilní. Návrh rozhraní jednotlivých politik Politiky musí skutečně implementovat dílčí kroky algoritmů a nikoliv jenom poskytovat konstanty apod., podle kterých se algoritmus bude řídit Ošetření výjimečných a chybových stavů Bezwarningová zkompilovatelnost (ideálně GCC i MSVC) Včastnost odevzdání Minimalizace duplicitního kódu (např. kód pro práci s řetězci by měl být napsaný pouze jednou tak, aby pracoval jak s char, tak s wchar_t

Odevzdání pouze přes Grupík Vše v jednom.zip souboru do sloupečku DU2 Součástí musí být soubor du2_main.cpp, který ukazuje použití knihovny, zbytek (tj. celou knihovnu) musí tvořit pouze hlavičkové soubory (vzhledem k použití šablon to jinak ani nepůjde). Jeden ze souborů knihovny musí mít jméno du2_parser.h, jehož includováním se zpřístupní všechny potřebné třídy a funkce knihovny. Posílejte pouze zdrojové soubory a neposílejte žádné makefile,.prj,.sln,.obj,.exe,.pdb a jakékoliv další vedlejší soubory za poslání nevhodného obsahu budou strhnuty body na začátek každého souboru vložte komentář typu // DU2-ARG.cpp // Karel Vomacka NPRG051 2010/2011

termín: středa 20.4 12:00 (poledne) Cíle: Vlastní kontejnery C++0x rvalue reference, move sémantika Zadání: Vytvořte chytrou hešovací mapu, která minimalizuje zbytečné kopírování pomocí C++0x rvalue referencí Třída musí podporovat standardní metody, musí umožnit uživatelům parametrizovat a nastavit její chování pomocí šablon

Vložení, smazání prvku, smazání všech prvků, základní iterátor operator [] s implicitním vložením prvku s default hodnotou operator= Poskytovat typy (key_type, value_type, data_type, iterator) Rehash při překročení load faktoru Klíčem jsou stadnardní typy (int, const char *, std::string, ) Vyhledávat v seřazeným poli Používat datové struktury jenom ze standardního stl Pokud to není vysloveně nutné, tak zamezit kopírování hešovací mapy (resp. jejich prvků)!

Typy key_type mapped_type Specifiká hešování parametrizován typem klíče hešovací funkci průměrný počet elementů v segmentu (bucket, slot) minimální počet segmentů porovnání dvou prvků (který je větší)

template<typename MapType, int SIZE> struct TestExample { void operator() () { MapType map; for ( size_t j = 0; j < SIZE; ++j ) map.insert( typename MapType::value_type(j,j) ); } };... TestExample<vase_implementace_mapy<int,int>,100> test; test(); template<typename key_type> struct hasher { enum { min_buckets = 8, bucket_size = 8 }; size_t operator()( const key_type& key ) const; bool operator()( const key_type& k1, const key_type& k2) const; }... TestExample<vase_implementace_mapy<int,int, hasher>,100> test; test();

template<typename MapType, int SIZE> struct TestPerformance { MapType create_another_map( const MapType& map ) { MapType new_map = map; for (size_t i = SIZE; i < 2*SIZE; ++i) new_map[i] = i; return new_map; } void operator() () { typedef std::vector<maptype> container_type; container_type container (SIZE, MapType()); for( size_t i = 0; i < SIZE; ++i) { MapType map; for ( size_t j = 0; j < SIZE; ++j ) map.insert( typename MapType::value_type(j,j) ); container[i] = create_another_map( map ); } } };

Podrobně popsat příklad II, proč se nepoužila RVO, kde se to zrychlilo a navrhnout komplexnější příklad ukazující zrychlení Naimplementovat swapy

C++0x VS2010 implicitne g++ -std=c++0x 1 test na 1 run - jinak hrozí zpomalení defragmentace paměti cache optimalizace kvalita hešovací funkce nebude předmětem hodnocení můžete použít již existující

Čistá a hezká syntaxe i sémantika Typy parametrů a návratových hodnot Ošetření výjimečných a chybových stavů Zrychlení oproti kopírující verzím Ošetření všech míst kde se zbytečně kopíruje Bezwarningová zkompilovatelnost Včastnost odevzdání

Pouze přes Grupík Vše v jednom.zip souboru do sloupečku DU3 Názvy zdrojáků (povinně): du3_map.h du3_main.cpp Posílejte pouze zdrojové soubory a neposílejte žádné makefile,.prj,.sln,.obj,.exe,.pdb a jakékoliv další vedlejší soubory za poslání nevhodného obsahu budou strhnuty body na začátek každého souboru vložte komentář typu // DU3-MAP.cpp // Karel Vomacka NPRG051 2010/2011

Termín: streda 4.5.2011 12:00 (poledne) Cíl: Sieťová komunikace via TCP Navrhnúť jednoduchý ale rozumný protokol dle specfikace Použiť multithreading (aspoň triviálne) Stačí jedna platforma Zadanie: Vytvorte uzly Seeder, Leecher a Tracker a navrhnite protokol dle specifikace tak, aby jeden Tracker vedel sprostredkovať viacerým Leecherom získanie súboru z viacerých Seederov. Očakáva sa, že jednotlivý Leecheri budu vymienať obsah aj medzi sebou.

Viditelnosť Každý Leecher vidí aspoň jeden Seeder Každý Leecher vidí Tracker Každý Leecher vidí aspoň zopár Leecherov Spolahlivosť V prostredí dochádza k výpadkom jednotlivých uzlov dočasne môžu všetky Seedery zmiznúť Tracker môže dočasne zmiznúť Leechery sa môžu objavovať a miznúť

FILE File je rozdelený na chunks rovnakej veľkosti Nodes si vymieňajú celé chunky atomická výmena Tracker Udržuje informácie o tom kto má aké chunks Leecher sa Trackeru dotazuje, kde môže získať chunks, ktoré mu chýbaju Seeder odpovedá a poskytuje Leecherom data Leecher poskytuje data iným Leecherom a sám sa snzí získať celý súbor. Tracker File File Leecher Leecher Seeder Seeder Seeder

Udržuje databázu rozdelenia chunkov v prostredí Nemusí mať explicitné spojenie so Seedermi je to ale vhodné, aby zbytočne nešíril nedostupné Seedery ako zdroj Trackovaný súbor je špecifikovaný v.torrent súbore nasledovne File name e.g. TheMovie.avi [null terminated string] File attributes Size [8B], hash [depends], #of chunks [8B] #chunk size [4B], chunk hash list [depends] Hash == (SHA1/CRC32, cokolvek) Seeder list URL:Port URL [null terminated string] PORT [2B]

Medzi Leecherom a Trackerom vznikne session ktorá by mala byť perzistetnou po určitú dobu (timeout) Tracker môže TCP spojenie ukončiť ak sa mu nezdá dosť aktívne po nejakú dobu Leecher môže ukončiť spojenie ak ho nepotrebuje (ale session NIE) Rozlišovať session a tcp spojenie Tracker prideluje IDčka Leecherom a Seederom Leecher sa musí u Trackeru zaregistrovať Správy HELO a BYEBYE - vznik session a ukončenie session Leecher Trackeru oznámi aké chunky má už stiahnuté Správa IHAVE navrhnite rozumne a nie tak bajvoko Leecher sa Trackera pýta na zdroje dát Správa INEED Leecher sa môže opýtať na konkrétne chunky ktoré chce Leecher môže špecifikovať koľko zdrojov chce Leecher môže nechať všetko na Trackera zistenie ktoré chunky nemá atď

Leecher môže Trackeru oznámiť, že niektoré zdroje sú preňho nedostupné Správa TOOFAR Tieto zdroje Tracker Leecherovi už nebude posielať Správa RESET zresetuje databázu nedostupných uzlov Leecher by mal Trackeru oznámiť ktoré chunky sa mu podarilo získať viz IHAVE Leecher sa smie pripájať len na obmedzený počet Seederov Toto reguluje Tracker pridelovacou politikou

Leecher sa môže pripojiť na Leechera/Seedera a žiadať chunky Pripojenie tiež via Session HELLO, BYEBYE Leecher oznami protajšku čo má k dispozícii IHAVE zoznam chunkov INEED zoznam požiadaviek GET ktorý chunk chce PUT posiela chunk na vyžiadanie Leecher/Seeder by mal zvládať rozumný nápor a rovnomerne obsluhovať všetky požiadavky Posielať len jednému a potom dalšiemu sa nepovažuje za rovnomerné

Implementácia Leechera, Seedera a Trackera Špecifikácia obsahu protokolu Torrent file (náš testovací súbor bude veľký) Rozumný návrh komunikácie, s prihliadnutím na nespolahlivost prostredia

Rozumná politika výberu zdrojov pre Leechra Aby nedostali všetci Leechri ten isty Seeder a ten sa zosype Parametrizovatelnost Multiplatformovost