abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
AbcLinuxu hledá autory!
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
dnes 12:22 | Pozvánky

Konference DevConf.CZ 2020, již dvanáctý ročník jedné z největších akcí zaměřených na Linux a open source ve střední Evropě, proběhne v Brně na FIT VUT od 24. do 26. ledna 2020. Zveřejněn byl program konference a spuštěna byla povinná a bezplatná registrace.

Ladislav Hagara | Komentářů: 0
včera 22:55 | Zajímavý článek

Příspěvek Prozkoumejte Česko na Wikidatech: druhý díl zajímavých Wikidata Queries na blogu Wikimedie Česká republika je volným pokračováním příspěvku Prozkoumejte Česko na Wikidatech: 10 nejlepších Queries datového žurnalisty z července 2017. Tentokrát jsou díky Wikidatům a SPARQL zobrazeny Železniční tratě v Česku, Díly pořadu Gebrian VS na mapě nebo Citace vědeckých článků – srovnání českých univerzit, na níž publikující autoři působí.

Ladislav Hagara | Komentářů: 0
včera 19:44 | Komunita

Ovlivnit Ubuntu 20.04 LTS lze vyplněním dotazníku. Více v příspěvku na blogu Ubuntu.

Ladislav Hagara | Komentářů: 0
včera 12:33 | Zajímavý článek

Na stránkách české komunity Fedory vyšel zajímavý článek o tom, jak se generují flatpakové balíčky z již existujících balíčků RPM.

KOLEGA | Komentářů: 7
včera 12:22 | Zajímavý článek

Donald Ervin Knuth, přední informatik a emeritní profesor na Stanfordově univerzitě, rozšířil svou mnohasvazkovou odbornou monografii The Art of Computer Programming (TAOCP), v českém překladu Umění programování, o Volume 4, Fascicle 5. Donald Knuth svou novou knihu představil ve své přednášce Pi and The Art of Computer Programming.

Ladislav Hagara | Komentářů: 32
8.12. 11:44 | Zajímavý článek

Arsenij Zinčenko v zápisku sdílí technické poznámky o tom, co je to „klíčenka“ (keyring) v Linuxu a v desktopovém prostředí, jak to souvisí se Secret Service a D-Bus, včetně příkladů. Význam těchto služeb spočívá v uložení a následném poskytování autentizačních údajů.

Fluttershy, yay! | Komentářů: 0
7.12. 01:44 | Nová verze

V únoru 2014 bylo hlasováním rozhodnuto, že výchozím init systémem v Debianu je systemd. V listopadu stejného roku bylo hlasováním rozhodnuto, že o podpoře dalších init systémů v Debianu není celoprojektové hlasování nutné. Po pěti letech už ale hlasování o init systémech a systemd nutné je. Vybírá se z 8 možností. Výsledek hlasování bude zveřejněn po 27. prosinci.

Ladislav Hagara | Komentářů: 22
7.12. 00:11 | Zajímavý článek

David Revoy, autor open source webového komiksu Pepper&Carrot, se rozhodl, že svůj komiks vydá také knižně a ve vlastní režii. Komiks již knižně vyšel ve francouzštině ve vydavatelství Glénat. David Revoy jej vydá v angličtině a použije pouze svobodný software. O své zkušenosti se dělí ve dvou příspěvcích na svém blogu. Z plánovaných dvou týdnů práce se staly dva měsíce. Vydání před Vánocemi se nestihne. Kontrolní výtisk má příliš jasné barvy, obrázky v knihách od Glénatu vypadají mnohem lépe, …

Ladislav Hagara | Komentářů: 1
6.12. 20:44 | IT novinky

Mezinárodní konsorcium W3C (World Wide Web Consortium) vydalo verzi 1.0 základní specifikace WebAssembly a po HTML, CSS a JavaScriptu prohlásilo WebAssembly za čtvrtý oficiální jazyk pro web.

Ladislav Hagara | Komentářů: 26
6.12. 13:33 | Komunita

Hlasování o obrázku v okně O Inkscapu 1.0 pokračuje druhým kolem. Ze 124 obrázků postoupilo do finálního hlasování 5 s nejvíce hlasy. Výsledek hlasování bude zveřejněn po jeho ukončení, tj. po 15. prosinci.

Ladislav Hagara | Komentářů: 8
Jaké hodinky nosíte (nejčastěji)?
 (23%)
 (5%)
 (17%)
 (54%)
Celkem 531 hlasů
 Komentářů: 135, poslední 6.12. 20:54
Rozcestník

www.AutoDoc.Cz

Dotaz: Návrhový vzor pro obalování C knihoven v C++

xkucf03 avatar 17.8. 18:45 xkucf03 | skóre: 48 | blog: xkucf03
Návrhový vzor pro obalování C knihoven v C++
Přečteno: 975×

I když se z C++ dají volat C knihovny přímo, přijde mi lepší si nad tím udělat třídy v C++, kterými se to obalí, což usnadní správu paměti a člověk nezapomene příslušný zdroj zavřít – prostě se to zavře samo, když skončí platnost proměnné.

C knihovny většinou vrací při otevření zdroje (např. USB zařízení) nějakou strukturu a na konci je potřeba ji poslat do nějaké close() funkce, kde se daná knihovna postará o uvolnění zdrojů. V C++ si tuto strukturu vytvoříme v konstruktoru a uložíme do instanční proměnné. V destruktoru pak zavoláme tu close() funkci. Tohle vypadá jednoduše a v funguje to… dokud instanci naší třídy nepředáme někam dál jako parametr, což způsobí kopii a následně se zavolá close() v destruktoru kopie, což nám ale znefunkční i původní instanci. Takže je potřeba zakázat kopírování (privátní kopírovací konstruktor) a objekt předávat jen referencí. Došel jsem k něčemu takovému, když jsem si hrál s USB HID API:

class HIDDevice {
private:
	hid_device* handle;

	HIDDevice(const HIDDevice& other) : handle(other.handle) {
	}

public:

	HIDDevice(unsigned short vendorId, unsigned short productId, const wchar_t *serialNumber) {
		int initError = hid_init(); // TODO: move to HIDContext class?
		if (initError) throw HIDException(L"Unable to init HID API.");
		handle = hid_open(vendorId, productId, serialNumber);
		if (handle == nullptr) throw HIDException(L"Unable to open HID device. Are you root? Is mouse present?");
	}

	virtual ~HIDDevice() {
		hid_close(handle);
		hid_exit(); // TODO: move to HIDContext class?
	}

	const std::wstring getManufacturerName() const {
		std::array<wchar_t, 200 > buffer;
		int error = hid_get_manufacturer_string(handle, buffer.data(), buffer.size());
		if (error) throw HIDException(L"Unable to get manufacturer name.");
		return buffer.data();
	}

	const std::wstring getProductName() const {
		std::array<wchar_t, 200 > buffer;
		int error = hid_get_product_string(handle, buffer.data(), buffer.size());
		if (error) throw HIDException(L"Unable to get product name.");
		return buffer.data();
	}

	void sendFeatureReport(std::vector<unsigned char> data) {
		int written = hid_send_feature_report(handle, data.data(), data.size());
		if (written < 0) throw HIDException(L"Unable to send feature report.");
	}

};

Je to takhle správně? Jaké jsou další možnosti?

Když si vystačím s předáváním referencí, tak mi to přijde funkční. Ale co když chci ten objekt předat někam dál a nechat ho tam žít, i když původní metoda skončí (a tím zanikne i ta původní proměnná)? Je potřeba to předávat obalené v chytrém ukazateli? To by fungovalo, ale přijde mi to trochu ošklivé, znepřehledňuje to kód. Víc by se mi líbilo, kdyby se předávala kopie a ten chytrý ukazatel si udržovala uvnitř. Znamená to tedy nutnost si udělat další třídu, která se bude spravovat v tom vnitřním chytrém ukazateli a bude držet jen to hid_device a ve svém destruktoru ho zavírat? Jak byste tohle řešili? V zásadě bych chtěl, aby se HIDDevice chovalo jako chytrý ukazatel a rád bych to implementoval nějak elegantně s minimem kódu.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes

Odpovědi

17.8. 20:02 MadCatX | skóre: 24 | blog: dev_urandom
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Použij C++11 a zkus něco jako toto:
/* Header */

class HIDDevice {
public:
	HIDDevice(const HIDDevice &other) = delete;
	HIDDevice(HIDDevice &&other);
	HIDDevice(unsigned short vendorId, unsigned short productId, const wchar_t *serialNumber);

	virtual ~HIDDevice();

private:
	hid_device *m_handle;

	/* ...impl... */
};

/* Implementace */

HIDDevice::HIDDevice(HIDDevice &&other) :
	m_handle{other.m_handle}
{
	other.m_handle = nullptr;
}

HIDDevice::~HIDDevice()
{
	if (m_handle != nullptr)
		cleanup();
}

Dále bych se vyhnul věcem jako unsigned short a použil přímo uint16_t, kde je velikost proměnné zaručena.
xkucf03 avatar 17.8. 20:23 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Dík, tohle vypadá trochu líp a přesouvací konstruktor mi tam chyběl.

To unsigned short jsem tam dal, protože to je typ, se kterým pracuje ta knihovna, to neovlivním. Maximálně bych to mohl přetypovávat a kontrolovat, že na sebe ty typy pasují.

Jinak bych ale potřeboval vyřešit tu otázku, co dělat, když nestačí předávání referencí. Tzn. vlastně jak nějak elegantně udělat z téhle třídy chytrý ukazatel. (můžu si udělat tu další třídu a dát chytrý ukazatel dovnitř HIDDevice, ale přijde mi to dost kostrbaté)

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
17.8. 21:00 MadCatX | skóre: 24 | blog: dev_urandom
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

To unsigned short jsem tam dal, protože to je typ, se kterým pracuje ta knihovna, to neovlivním. Maximálně bych to mohl přetypovávat a kontrolovat, že na sebe ty typy pasují.

V C++11 jde udělat třeba
static_assert(sizeof(unsigned short) == sizeof(uint16_t), "Mismatching unsigned short vs. uint16_t sizes"));

Jinak bych ale potřeboval vyřešit tu otázku, co dělat, když nestačí předávání referencí. Tzn. vlastně jak nějak elegantně udělat z téhle třídy chytrý ukazatel. (můžu si udělat tu další třídu a dát chytrý ukazatel dovnitř HIDDevice, ale přijde mi to dost kostrbaté)

A kdy přesně by to nemělo stačit? S move konstruktory a referencemi jde IMHO vyřešit většina případů. V případě, kdy je nutné sdílet nějaký zdroj z více míst lze použít std::shared_ptr. Pro objekty, na které je naopak nutné ukazovat právě jednou existuje std::unique_ptr.
xkucf03 avatar 17.8. 22:46 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
S move konstruktory a referencemi jde IMHO vyřešit většina případů.

Když tu instanci budu vracet jako návratovou hodnotu, tak to bude OK, ale když budu muset někde použít std::move() tak mi v té proměnné zůstane stará instance v nějakém divném/nepoužitelném stavu, přitom na ní ale půjdou volat metody (akorát to asi za běhu bude padat nebo se chovat divně), ne? To už mi přijde lepší to počítání referencí.

V případě, kdy je nutné sdílet nějaký zdroj z více míst lze použít std::shared_ptr.

Ano, ale obalovat všechno do std::shared_ptr mi přijde ošklivé – v signaturách metod to působí jako bordel a znepřehledňuje to kód. Elegantnější by mi přišlo, kdyby ten chytrý ukazatel byl schovaný uvnitř toho HIDDevice.

Ještě mě napadá tohle:

using HIDDevice_p = std::shared_ptr<HIDDevice>;

HIDDevice_p x(new HIDDevice(0,0,0));
x->getProductName();

To už vypadá trochu líp, když tam nestraší všude std::shared_ptr.

BTW: existuje nějaká konvence pro pojmenování takových typů? (jako jsem tady použil to _p)

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
17.8. 23:08 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
ale když budu muset někde použít std::move() tak mi v té proměnné zůstane stará instance v nějakém divném/nepoužitelném stavu, přitom na ní ale půjdou volat metody (akorát to asi za běhu bude padat nebo se chovat divně), ne?
No, ten vnitřní pointer bude nullptr. Jinak ano, tohle je bohužel velký problém move semantics C++, které kvůli tomu vlastně ani pořádně move semantics nejsou, spíš je to takový swap s prázdným objektem. Ale nedá se s tím nic moc dělat.

V praxi to můžeš brát tak, že přistupovat k objektu po std:move() není validní. Je to podobné jako třeba invalidace iterátorů u kontejnerů a podobně. Prostě se vynasnažit po move k proměnný nepřistupovat.
17.8. 23:32 MadCatX | skóre: 24 | blog: dev_urandom
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
S move konstruktory a referencemi jde IMHO vyřešit většina případů.

Když tu instanci budu vracet jako návratovou hodnotu, tak to bude OK, ale když budu muset někde použít std::move() tak mi v té proměnné zůstane stará instance v nějakém divném/nepoužitelném stavu, přitom na ní ale půjdou volat metody (akorát to asi za běhu bude padat nebo se chovat divně), ne?

Nezůstane. Move se používá v případě, že daný objekt v nějakém kontextu už nepotřebuješ ale zároveň je potřeba, aby dál existoval někde jinde. Provádět move na proměnných, ke kterým by mohlo chtít něco později přistupovat ze stejného kontextu je samozřejmě špatně.

To už mi přijde lepší to počítání referencí.

Refcounting řeší trochu jiný problém. Move ti umožňuje vytvořit právě jednu instanci nějaké třídy a tu různě přesouvat, aniž by se kdy zkopírovala, zničila a vytvořila znovu atp.
V případě, kdy je nutné sdílet nějaký zdroj z více míst lze použít std::shared_ptr.

Ano, ale obalovat všechno do std::shared_ptr mi přijde ošklivé – v signaturách metod to působí jako bordel a znepřehledňuje to kód. Elegantnější by mi přišlo, kdyby ten chytrý ukazatel byl schovaný uvnitř toho HIDDevice.

Tím si tam ale nacpeš další úroveň indirekce, která je v principu zbytečná.

Ještě mě napadá tohle:

using HIDDevice_p = std::shared_ptr<HIDDevice>;

HIDDevice_p x(new HIDDevice(0,0,0));
x->getProductName();

To už vypadá trochu líp, když tam nestraší všude std::shared_ptr.

BTW: existuje nějaká konvence pro pojmenování takových typů? (jako jsem tady použil to _p)

Od C++11 dále existuje auto, které je možné psát místo konkrétního jména typu. Nelze použít úplně všude ale velkou část explicitního vypisování typů ušetří. Vytvořit si alias na nějaké často používaný typy, jak navrhuješ, je samozřejmě dobrý nápad.
17.8. 23:53 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
To už mi přijde lepší to počítání referencí.
K tomu mě napadá, že si můžeš ten std::shared_ptr hodit naopak dovnitř té třídy HIDDevice - obalit tím ten C API handle s tím, že bys ten std::shared_ptr vytvořil s tou uvolňovací C API funkcí coby custom deleterem. Pak by ta třída HIDDevice mohla být kopírovací a fungovalo by to korektně. Ale za cenu refcountingu a indirekce. Nedělal bych to, pokud to není nutný.
18.8. 00:06 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Tzn. v kódu nějak takhle:

class HIDDevice {
private:
    std::shared_ptr<hid_device> m_handle;

public:
    HIDDevice(unsigned short vendorId, unsigned short productId, const wchar_t *serialNumber) {
        auto *handle = hid_open(vendorId, productId, serialNumber);
        if (handle == nullptr) {
            throw HIDException(L"Unable to open HID device. Are you root? Is mouse present?");
        }

        m_handle.reset(handle, hid_close);
    }

    // ...
};

Ale jak říkám, nedělat to, pokud sdílenej přístup z více míst není potřeba ;-)
xkucf03 avatar 18.8. 00:20 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Ono kupodivu funguje i tohle:
handle.reset(hid_open(vendorId, productId, serialNumber), hid_close);
if (handle == nullptr) throw HIDException(L"Unable to open HID device. Are you root? Is mouse present?");
Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
19.8. 10:12 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Jo, můžeš to i zkrátit tím, že shared_ptroperator bool(), kterej slouží na tohle.

Nicméně doporučuju psát tak, aby to bylo pokudmožno čitelný, tzn. třeba nedávat moc logiky do jednoho řádku... Tohle je samozřejmě otázka stylu a ne nějaká objektivní pravda... Osobně jsem fanoušek stylu psát závorky { ... } okolo if, for, while atd. bloků vždy, aby byl kód pokudmožno jednoznačný.
xkucf03 avatar 18.8. 00:13 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Dík, to je dobrý nápad.

Chápu, že každý z těch přístupů má něco do sebe, budu si pamatovat oba a zkusím používat, co se kde hodí :-) I když zrovna v tom, co teď píšu, je to úplně jedno – je to jen jednoúčelový prográmek k nakonfigurování myši.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
xkucf03 avatar 17.8. 21:00 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Z vedlejší diskuse:

1) Já bych začal tím, že bych rozhodně nikdy nepoužíval typ uint16t. A to jednoduše proto, že takový typ na mnoha platformách vůbec nemusí existovat. Pokud platforma/procesor nedokáže poskytnout 16bitový integer, tak prostě v C/C++ vůbec uint16t neexistuje.

Zrovna 16bitový integer na mnoha 32bitových platformách vůbec neexistuje.

Ty typy moc neovlivním, vychází z té céčkové knihovny… Kdybych to chtěl víc řešit, tak si udělám nějakou abstrakci a víc to zobecním, aby časem šla použít třeba jiná knihovna, a pak bych použil i vlastní typy. Ale tady se to snažím dělat víceméně 1:1, jen objektově.

2) Jinak pro určování životnosti existuje věc, které se říká počítání referencí. Pokud dané třídě dáte statickou proměnnou s počtem kopií odkazu, řeší to problém. Jakmile počet odkazů klesne na nulu, zrušíte i zdroj. Pokud chcete mít multithreading prostředí pro inkrementaci a dekrementaci čítače referencí použijete atomické funkce.

Vytvoření kopie třídy inkrementuje čítač referencí. Destruktor třídy pak dekrementuje čítač referencí, a pokud dosáhl nuly, tak zavře handle.

Což znamená implementovat vlastní chytrý ukazatel. Jako cvičení si to asi zkusím… ale že by se mi to chtělo dělat pokaždé, když chci obalit C knihovnu do C++, to se říct nedá. Je to běžný a doporučený postup?

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
17.8. 21:20 MadCatX | skóre: 24 | blog: dev_urandom
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Z vedlejší diskuse:

1) Já bych začal tím, že bych rozhodně nikdy nepoužíval typ uint16t. A to jednoduše proto, že takový typ na mnoha platformách vůbec nemusí existovat. Pokud platforma/procesor nedokáže poskytnout 16bitový integer, tak prostě v C/C++ vůbec uint16t neexistuje.

Zrovna 16bitový integer na mnoha 32bitových platformách vůbec neexistuje.

Ty typy moc neovlivním, vychází z té céčkové knihovny… Kdybych to chtěl víc řešit, tak si udělám nějakou abstrakci a víc to zobecním, aby časem šla použít třeba jiná knihovna, a pak bych použil i vlastní typy. Ale tady se to snažím dělat víceméně 1:1, jen objektově.

Ponkrácovsky korektní kód by se dal napsat třeba takto
static_assert(sizeof(unsigned short) >= sizeof(uint_least16_t), "Mismatching unsigned short vs. uint_least16_t sizes");
a používat uint_least16_t.

2) Jinak pro určování životnosti existuje věc, které se říká počítání referencí. Pokud dané třídě dáte statickou proměnnou s počtem kopií odkazu, řeší to problém. Jakmile počet odkazů klesne na nulu, zrušíte i zdroj. Pokud chcete mít multithreading prostředí pro inkrementaci a dekrementaci čítače referencí použijete atomické funkce.

Vytvoření kopie třídy inkrementuje čítač referencí. Destruktor třídy pak dekrementuje čítač referencí, a pokud dosáhl nuly, tak zavře handle.

Což znamená implementovat vlastní chytrý ukazatel. Jako cvičení si to asi zkusím… ale že by se mi to chtělo dělat pokaždé, když chci obalit C knihovnu do C++, to se říct nedá. Je to běžný a doporučený postup?

Rozhodně není. C++11 má plný komplement automatických ukazatelů, které stačí prostě použít.
17.8. 21:39 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Což znamená implementovat vlastní chytrý ukazatel. Jako cvičení si to asi zkusím…
Nic takového prosímtě nedělej, ta Ponkrácova rada je úplne mimo, zbytečně tam zanáší další indirekci, navíc přes holý pointer. Dobře ti radí MadCatX - potřebuješ move konstruktor. Ještě bych analogicky doimplementoval move operator=() a potom kopírovací operator=() označil delete stejně jako to je s kopírovacím konstruktorem.

unsigned short úplně klidně používej, pokud to je typ, který používá to C API.

Pokud k tomu budeš potřebovat přistupovat nějak sdíleně, použij nejspíše shared_ptr, ale nejdříve bych se rozmyslel, jestli to je opravdu potřeba.
17.8. 21:36 debian+
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
18.8. 11:33 jdsulin2
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
V zásadě bych chtěl, aby se HIDDevice chovalo jako chytrý ukazatel a rád bych to implementoval nějak elegantně s minimem kódu.
Tak to proste pouzij jako chytry ukazatel (std::shared_ptr) pri predani se proste jenom zvysi counter a kdyz to klesne na 0, tak se zavola destruktor. Nebo primo na tom hid_device, ale tam uz budes potrebovat deleter.
19.8. 21:47 hz
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
To s tim si dat jako member misto hid_device* std::shared_ptr, ci std::unique_ptr dle toho, zda chci ci nechci povolit kopii, uz tu receno bylo. Nemusis pak psat ani destruktor a move konstruktor, implicitni by se mel vytvorit sam. Musi tedy byt specifikovany deleter tech smart pointeru...

U const std::wstring getManufacturerName() const je zbytecne ten prvni const psat, ale to by ti mel rict prekladac. A hlavne bych na to pustil valgrind, neb mi prijde, ze v implementaci vracis data toho std::array, kteremu se hned zavola destruktor.

A jinak ano, pokud jsi v C++ s vyjimkama, je RAII v podstate nutnost.
19.8. 22:34 Jardík
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
ze v implementaci vracis data toho std::array, kteremu se hned zavola destruktor
To je ok, protože nejprve se vytvoří wstring, ten si spočte velikost řetězce (předpokládá ukončovací nulu), naalokuje si buffer, data zkopíruje a teprve poté zaniká původní std::array.
20.8. 23:20 hz
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Jiste ze je, psat neco unavenej... Nejak jsem se neodprostil od toho ze je tam wstring a ne ten pointer na data. Dik za upresneni.
19.8. 22:28 Jardík
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Otázkou je, jakou životnost by tento objekt měl mít. Pokud s ním vždy hodláš pracovat např. uvnitř jediné funkce v rámci nějakého bloku, kdy na začátku otevřeš zařízení a na konci uzavřeš, je nejlepší řešení to nejjednodušší - v konstruktoru otevřeš, v destruktoru zavřeš, neřešíš žádné smart pointery, žadný move, žádný copy. Do případné další funkce objekt prostě předáš referencí nebo ukazatelem.

Chceš-li objekt sdílet mezi různými vlastníky a chceš, aby se prostředek uvolnil, až nebude potřeba, použiješ shared_ptr. Tam máš dvě možnosti, které si zvolíš podle velikosti objektu, nebo podle snadnosti použití tvého objektu. 1) V případě, kdy je objekt jen jediný ukazatel, tak bych byl pro to, aby se shared_ptr použil na ten handle a shared_ptr by byl uvnitř tvé třídy. Tj. něco podobného, jako je v Qt u kontejnerů apod, a jako ti už tady někdo doporučil. Při takto malém objektu nedoporučuji alokovat C++ objekt na haldě a pracovat s ním jako shared_ptr, je to zbytečné a neefektivní (pokud jich plánuješ vytvářet hodně, třeba v cyklu). 2) Naopak, pokud by byl objekt velký a nechtěl si použít PImpl (najdi na wiki), tak klidně pracovat s instancí ve shared_ptr.

Nechceš-li objekt sdílet, ale chceš-li mít možnost přesouvat vlastnictví, tak buď unique_ptr, nebo v tomto případě, kdy máš pouze jeden pointer, tak vytvořit move operator a ctor, zakázat kopírovaní. V dtoru je třeba ošetřit stav, kdy je z objektu přesunut pointer do jiného, pokud se o to nepostará uvolňující funkce (např. pro delete netřeba testovat na nullptr).
xkucf03 avatar 20.8. 16:15 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Takže ty možnosti vlastně jsou:

  • Nedovolit kopírování a používat jen z jednoho místa nebo přesouvat případně předávat referenci či ukazatel.
  • Dát si chytrý ukazatel dovnitř toho HIDDevice a obalit jím jen ten to hid_device případně si počítat reference ručně. O tomhle jsem věděl, ale nenapadlo mne, že není potřeba dělat další třídu a do toho chytrého ukazatele jde dát rovnou to hid_device + nastavit mazací funkci.
  • Obalit chytrým ukazatelem celou instanci HIDDevice a předávat tu. To mi původně přišlo ošklivé, ale je fakt, že když si uděláme alias, tak to už vypadá v pohodě.

V programu, který teď píši, bych si vystačil s první možností, ale cílem otázky bylo spíš si ujasnit možnosti a vybrat nějaké široce použitelné řešení – dejme tomu, že bych psal obecné C++ API pro nějakou knihovnu a nevím, jak ho kdo bude chtít používat a jestli si vystačí s předáváním ukazatelů/referencí nebo zda bude chtít sdílet vlastnictví… Z pohledu uživatele mi přijde nejpohodlnější ta druhá možnost.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
20.8. 17:01 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
V programu, který teď píši, bych si vystačil s první možností, ale cílem otázky bylo spíš si ujasnit možnosti a vybrat nějaké široce použitelné řešení – dejme tomu, že bych psal obecné C++ API pro nějakou knihovnu a nevím, jak ho kdo bude chtít používat a jestli si vystačí s předáváním ukazatelů/referencí nebo zda bude chtít sdílet vlastnictví… Z pohledu uživatele mi přijde nejpohodlnější ta druhá možnost.
IMO obecně je lepší poskytovat unique ownership objekty, protože když bude uživatel potřebovat, tak si to obalí do share_ptr, případně může třeba chtít použít i nějaké vlastní smart pointery (share_ptr není až taková výhra pro multithreading). Tj. dává mu to širší možnost z hlediska composability. Když poskytneš pouze ref-counted objekt, tak s tím uživatel nemůže nic moc dělat - ten ref-counting z vnitřku neodstraní.

Konkrétně v tomhle případě je trochu pitomý, že to C API vyžaduje pointer, takže když napíšeš tu unique ownership variantu a následně to dáš do shared_ptr, bude tam zbytečně dvojtá indirekce. Takže leda mít obě varianty.
20.8. 17:02 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
s/share_ptr/shared_ptr/
20.8. 17:59 MadCatX
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Souhlasil bych s tím, že obecně je výhodné postupovat od jednoduchých prvků ke složitějším, takže ve většině případů bych asi udělal HIDDevice nekopírovatelný a obalil ho sdíleným ukazatelem. Pak jsou zde ovšem situace, kdy může být vhodnější poskytovat už rovnou nějaký těžkotonážní wrapper, který bude řešit třeba konkurenci a podobně. Jako vždycky platí, že to nejsprávnější řešení vždy závisí na konkrétní situaci.
19.8. 22:31 Jardík
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
void sendFeatureReport(std::vector<unsigned char> data)
Doporučím předat vector const referencí: const std::vector<unsigned char>& data Ušetříš spoustu alokací, pokud to budeš volat v cyklu a zároveň neplánušej vytvářet nový vektor pro každé volání (a tedy mít možnost použít move ctor vektoru).
xkucf03 avatar 20.8. 15:51 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Dík, const & jsem přidal, pořád na to někdy zapomínám.
Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
xkucf03 avatar 21.8. 13:28 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Celé zdrojáky jsou tady: cadMousePro, kdyby na to chtěl někdo kouknout.
Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
22.8. 20:05 c++++
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
diky jsem bulimik a uz si nemusim strkat prsty do krku
xkucf03 avatar 22.8. 22:58 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Jestli k tomu máš nějaké konkrétní připomínky, tak sem s nimi, rád to vylepším.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
26.8. 17:20 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Nic konkrétního nemá; koneckonců je to anonym. :-D

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
26.8. 18:01 MadCatX | skóre: 24 | blog: dev_urandom
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Tak nějak zběžně jsem na to mrknul a napadá mě toto.

Máš nějaký zvláštní důvod používat wide stringy? Smysl by to mělo na Windows, které interně používají UTF-16. Většina linuxových terminálů používá buď UTF-8 nebo nějaké single-byte kódování. Ušetříš si tím ty konverze tam a zpět.

Dál mi přijde trochu zvláštní mít exit code ve výjimce. Pokud potřebuješ vracet exit cody jiné než EXIT_SUCCESS a EXIT_FAILURE, asi bych je necpal přímo do té výjimky ale podle typu výjimky, co probublá až nahoru rozhodl, co se má vrátit.

Na parsování argumentů z příkazové řádky existuje getopt() a spol. (Widle nic podobného nemají, což mě při psaní čehokoliv pro Win32 vždycky děsně prudilo).

Místo push_back se s C++11 a výše doporučuje používat emplace_back. Rozdíl je následující:
#include <vector>


class Pig {
public:
    Pig(int weight, bool male) :
	    m_weight{weight},
	    m_male{male}
    {}

private:
    int m_weight;
    bool m_male;
};

int main()
{
	std::vector<Pig> swines{};

	swines.push_back({120, false});
	swines.emplace_back(120, false);
	swines.push_back(120, false); /* Chyba */

	return 0;
}
Append do vektoru s použitím emplace_back může použít "in-place" inicializaci, kdy nedochází ke zbytečnému vytvoření dočasného objektu, který se pak zkopíruje.

Místo
Frequency frequency = Frequency::Hz_1000;
se v C++11 doporučuje
auto frequency = Frequency::Hz_1000;
Herb Sutter na téma extenzivního používaní auto měl na CppConu přednášku, skoro mám dojem, že to byla tahle. Pro jednoduché typy se to zdá jako zbytečnost ale jakmile budeš mít kód jako tohle
std::shared_ptr<std::vector<Pig>> getSwines();

auto swines = getSwines();
začne to mít smysl.

V C++11 ještě existuje nová sytaxe volání konstrukorů složenými závorkami:
Pig peppa{66, false};
V případě, že se volá konstruktor bez argumentů, považuje se za idiomatický zápis str::string s{}.
26.8. 18:32 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
Smysl by to mělo na Windows, které interně používají UTF-16.
Bohužel ani to ne. Windows nepodporují pořádně UTF-16, ale spíš jakýsi prasopes mezi UCS-2 a UTF-16, tady na HN to někdo nazval UCS2 + surrogates, což mi přijde výstižný.

wchar_t a std::wstring je odvozený z toho Windowsího modelu (a bohužel také stringové typy v Qt, Javě, JavaScriptu, ...) a v podstatě mi nepřijde užitečný vůbec k ničemu. Korektní podporu Unicode nezajistí, principielně to je podobný jako mít UTF-8 data v std::stringu. Asi kdyby člověk psal Windows-specific software používající Windows API, tak by to třeba smysl mělo, že by se nemuselo konvertovat sem a tam, ale v multiplatformním SW vzniklém na Unixu IMO nemá wchar_t vůbec žádnou výhodu.
xkucf03 avatar 26.8. 19:22 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

A co takový printf() a zarovnávání textu pod sebe? Ono sice ani wprintf() nefunguje vždy1, ale většinou ano – zatímco printf() se std::string / char se rozsype i na naprosto základních znacích (např. čeština s diakritikou) kvůli tomu, že ty znaky jsou vícebajtové.

Kromě toho jsem už tolikrát zažil špatně napsané programy, které implicitně předpokládaly nějaké kódování a mršily data... takže za správný postup považuji explicitní konverze resp. měl bys to mít pod kontrolou a vědět, v jakém kódování je vstup, v jakém výstup… a typicky to vede na konverzi na nějakou interní reprezentaci.2 Ten wchar_t není ideální, ale v rámci standardní knihovny je to asi jediná jakž takž použitelná možnost. Různé knihovny to dělají různě, Qt má něco svého, Apache Xerces má char16_t, takže se to pak bohužel konvertovat musí sem tam. Zlatá3 Java, kde je prostě String a tuhle třídu používají všechny knihovny.

[1] u znaků, které mají na obrazovce šířku dvou znaků, což je IMHO trochu prasárna samo o sobě
[2] případně mít nějakou lehkou abstrakci, která by se obešla bez konverzí v případě, že vše bude v jednom kódování… ale tam je otázka, jestli to jen přeposílat dál jako surové bajty, nebo jestli to nějak validovat a zajistit, aby to byl platný řetězec v daném kódování
[3] ať mě nikdo nechytá za slovo: i tam samozřejmě lze narazit na problémy, ale to už je dané spíš složitostí Unicodu jako takového – pořád je na tom Java řádově lépe než C++

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
26.8. 20:07 kralyk z abclinuxu | skóre: 29 | blog:
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
A co takový printf() a zarovnávání textu pod sebe? Ono sice ani wprintf() nefunguje vždy1, ale většinou ano – zatímco printf() se std::string / char se rozsype i na naprosto základních znacích (např. čeština s diakritikou) kvůli tomu, že ty znaky jsou vícebajtové.
Tohle ale nesouvisí s kódováním UTF-8 vs UTF-16. Pro výpočet šířky znaku (třeba "tradičně" pomocí wcwidth()) stejně potřebuješ iterovat znaky v UTF-32. Všimni si, že wcwidth() implementace podporující korektně celý rozsah Unicode počítají s 32-bitovým wchar_t, potažmo UTF-32. V zásadě by tam měl být spíš nějaký odpovídající integer. Ten typ wchar_t jakož i vlastně název funkce jsou matoucí, resp. pozůstatek historie.

Zkus si třeba jak ti to funguje s tímto stringem, kde znaky by měly mít šířku 1.
Kromě toho jsem už tolikrát zažil špatně napsané programy, které implicitně předpokládaly nějaké kódování a mršily data... takže za správný postup považuji explicitní konverze resp. měl bys to mít pod kontrolou a vědět, v jakém kódování je vstup, v jakém výstup… a typicky to vede na konverzi na nějakou interní reprezentaci.
Ano, ale pointa je, že interní reprezentace pomocí wide stringů není o nic lepší než pomocí narrow stringů - ani jedno nepodporuje Unicode. Oboje jsou bloby obecných dat.
Zlatá Java, kde je prostě String a tuhle třídu používají všechny knihovny.
Co je to platné, když ta třída String nezaručuje validitu Unicode obsahu a může klidně obsahovat nevalidní data? Jaký je kvalitativní rozdíl mezi std::string a Java String? Obojí je shluk random bajtů, resp. to druhé je shluk random 16-bitových hodnot, které mohou a nemusí být validní Unicode. Ten rozdíl je pouze kvantitativní: Java String většinou obsahuje validní Unicode. Což je lepší nebo horší, podle toho, jak se na to díváš.
xkucf03 avatar 26.8. 19:48 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Máš nějaký zvláštní důvod používat wide stringy? Smysl by to mělo na Windows, které interně používají UTF-16. Většina linuxových terminálů používá buď UTF-8 nebo nějaké single-byte kódování. Ušetříš si tím ty konverze tam a zpět.

viz #37

Dál mi přijde trochu zvláštní mít exit code ve výjimce. Pokud potřebuješ vracet exit cody jiné než EXIT_SUCCESS a EXIT_FAILURE, asi bych je necpal přímo do té výjimky ale podle typu výjimky, co probublá až nahoru rozhodl, co se má vrátit.

Tohle jsem vykopíroval z jiného svého projektu, kde se to jmenuje CLIException a ty chybové kódy jsou z nějakého číselníku, který jim dává význam. Souhlasím, že by to bylo čistější řešit přes nějakou hierarchii tříd a z ní odvozovat ty návratové kódy, ale nechtělo se mi s tím v C++ dělat, takže jsem to trochu zjednodušil.

Na parsování argumentů z příkazové řádky existuje getopt() a spol.

getopt() vím, ale přijde mi, že by mi nijak nepomohlo – já totiž nepoužívám krátké varianty, ani nepotřebuji brát -abc jako ekvivalent -a -b -c nebo --arg=param jako --arg param. A ta práce, která zbude spočívá v konverzi pole řetězců na stromovou strukturu objektů a to už je specifické pro každou aplikaci a žádná knihovna s tím nepomůže (dovedu si představit elegantní deklarativní řešení pomocí anotací v Javě).

Místo push_back se s C++11 a výše doporučuje používat emplace_back.

Dík, vyzkouším.

Herb Sutter na téma extenzivního používaní auto měl na CppConu přednášku, skoro mám dojem, že to byla tahle. Pro jednoduché typy se to zdá jako zbytečnost ale jakmile budeš mít kód jako tohle

Na auto si bohužel často vylámou zuby editory/IDE a přestane fungovat napovídání, takže to už tam raději uvedu typ explicitně. Někdy používám aliasy pomocí using, kterými si nějaký složitější/generický typ pojmenuji nějak lépe. S tím napovídání nemá problém.

V případě, že se volá konstruktor bez argumentů, považuje se za idiomatický zápis str::string s{}.

V čem je to lepší? Podle Effective modern C++ by zápisy Abc a;Abc a{}; měly zavolat výchozí konstruktor. Ty závorky mi tam přijdou navíc.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
27.8. 10:53 MadCatX
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Na auto si bohužel často vylámou zuby editory/IDE a přestane fungovat napovídání, takže to už tam raději uvedu typ explicitně. Někdy používám aliasy pomocí using, kterými si nějaký složitější/generický typ pojmenuji nějak lépe. S tím napovídání nemá problém.

V tom případě ti doporučuji použít lepší IDE :) Správné našeptávání pro C++11 a výše je náročnější na schopnost překladače pochopit psaný kód. Nový KDevelop nebo QtCreator s Clang Code Modelem s tím problém nemají a zvládají spoustu dalších věcí, např. online statickou analýzu apod. Osobně bych se v NetBeans s C++ asi nenervoval...
V případě, že se volá konstruktor bez argumentů, považuje se za idiomatický zápis str::string s{}.

V čem je to lepší? Podle Effective modern C++ by zápisy Abc a;Abc a{}; měly zavolat výchozí konstruktor. Ty závorky mi tam přijdou navíc.

Výhoda je v té idiomatičnosti, tedy že volání konstruktoru vypadá vždycky stejně. Mišmaš ze starého C++, kde Abc x; i Abc x(1, 2, 3); volá konstruktor je trochu rušivý. V C++11 lze zavolat konstruktor i takto, je-li znám typ, který se má vrátit.
class Dog {
public:
  Dog(std::string breed, float age);
};

Dog getPuppy()
{
  return {"Dachshund", 0.3};
}
xkucf03 avatar 27.8. 10:59 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
V tom případě ti doporučuji použít lepší IDE :) Správné našeptávání pro C++11 a výše je náročnější na schopnost překladače pochopit psaný kód. Nový KDevelop nebo QtCreator s Clang Code Modelem s tím problém nemají a zvládají spoustu dalších věcí, např. online statickou analýzu apod. Osobně bych se v NetBeans s C++ asi nenervoval...

Až na to, že Netbeans napovídají lépe než QtCreator (aspoň ta verze, která je v Ubuntu LTS). A to i když to srovnávám s Netbeans 8.2 z roku 2016.

Další věc je integrace s verzovacími systémy, kterou nikdo lepší než Netbeans AFIK nemá.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
27.8. 12:19 MadCatX
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
V Bionicu by měl být QtCreator 4.5.2. Myslím, že tam byl Clang Code Model v nějaké dost experimentální verzi a za moc nestál. Verze 4.9 je na tom o mnoho lépe. CCM se musí povolit v pluginech, ve 4.5 bude ten plugin nejspíš standardně vypnutý. Integraci a VCS nemůžu posoudit, protože v NB jsem ji snad nikdy nepoužíval. QtCreator se s běžnými VCS integruje taky, zda lépe či hůře než NB fakt netuším...

Aktuální QtCreator se dá stáhnout i jako zkompilovaná binárka z qt.io, kdybys to chtěl „nezávazně“ vyzkoušet.
xkucf03 avatar 27.8. 13:31 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Asi zkusím tu novou verzi, ale primárně se snažím používat balíčky dostupné v rámci distribuce.

VCS nemůžu posoudit, protože v NB jsem ji snad nikdy nepoužíval. QtCreator se s běžnými VCS integruje taky, zda lépe či hůře než NB fakt netuším...

Za zásadní vlastnost považuji to, že mi Netbeans barevně označují řádky, které byly změněny/přidány/smazány oproti poslední verzi a umožňují vracet jednotlivé změny. Prohlížeč historie se taky hodí (to dost používám, když dělám revize). Jinak to kombinuji s příkazovou řádkou a třeba hg statushg commit dělám často raději tam, ale na některé věci je GUI nenahraditelné a příkazová řádka mu nemůže konkurovat.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
xkucf03 avatar 2.9. 13:41 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Tak jsem zapnul ten plug-in v QtCreatoru a napovídá o dost líp. Akorát mi tam vadí, že šířka toho vyskakovacího okna není pevně daná podle nejdelší metody, ale dynamicky se mění podle toho, jak dlouhé názvy to zrovna napovídá, takže to při listování všelijak poskakuje. Taky tam chybí dokumentace, alej jinak celkem OK.

U těch Netbeans mi teď funguje i to auto – správně to odvodí typ a napovídá. Dřív to někdy zlobilo. Mám podezření, že to občas najde nějakou jinou verzi standardní knihovny nebo to nějak špatně vyhodnotí některá makra, což pak má za následek, že to určité věci přestane napovídat.

P.S. samozřejmě že nejde jen o napovídání, ale i o podtrhávání chyb v kódu – oboje pracuje nad stejným modelem.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
26.8. 17:46 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

No, já jsem se k tomuhle vláknu dostal naneštěstí až takhle pozdě, takže už další odpověď asi nikoho nezajímá. Ale mě zase nezajímá, že to nikoho nezajímá, takže si tu zkrátka uprdnu následující:

#include <iostream>
#include <string>

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

namespace {
class FileOverwriter {
 public:
  FileOverwriter(const std::string& pathname)
      : path{pathname}, fd{[this]() {
          const int result{open(path.c_str(),
                                O_CREAT | O_WRONLY | O_TRUNC,
                                S_IRUSR | S_IWUSR)};
          if (result == -1) throw errno;
          std::cerr << "opened '" << path << "'\n";
          return result;
        }()} {}

  FileOverwriter(FileOverwriter&& old) : path(old.path), fd(old.fd) {
    old.fd = -1;
  }

  ~FileOverwriter() {
    if (fd != -1) {
      if (close(fd) == -1) {
        std::cerr << strerror(errno) << std::endl;
      }
      std::cerr << "still owning '" << path
                << "', closing it for real\n";
    } else {
      std::cerr << "no longer owning '" << path << "', not closing\n";
    }
  }

  FileOverwriter& operator<<(const std::string& what) & {
    write_to_fd(what);
    return *this;
  }

  const FileOverwriter& operator<<(const std::string& what) const& {
    write_to_fd(what);
    return *this;
  }

  FileOverwriter operator<<(const std::string& what) && {
    write_to_fd(what);
    return std::move(*this);
  }

 private:
  void write_to_fd(const std::string& what) const {
    const char* bufpos = what.c_str();
    size_t to_write = what.size();
    for (;;) {
      const ssize_t written = write(fd, bufpos, to_write);
      if (written == -1) throw errno;
      to_write -= written;
      if (!to_write) break;
      bufpos += written;
    }
  }

  const std::string path;
  int fd;
};

void WriteFooterAndClose(FileOverwriter o) {
  o << "this is at the end\n";
}

FileOverwriter WriteSomethingAndGiveItBack(FileOverwriter o) {
  o << "this is somewhere in the middle\n";
  return o;
}

void JustWriteBlahBlah(const FileOverwriter& o) {
  o << "blah\n"
    << "blah\n";
}
}  // namespace

int main() try {
  // A few std::move() experiments on /tmp/first.
  FileOverwriter o{"/tmp/first"};
  o << "o says blah\n";
  FileOverwriter p{std::move(o)};
  p << "p says blah\n";
  FileOverwriter q{WriteSomethingAndGiveItBack(std::move(p))};
  q << "q says blah\n";
  WriteFooterAndClose(std::move(q));

  // A somewhat temporary FileOverwriter on /tmp/second.
  FileOverwriter("/tmp/second") << "cheche\n"
                                << "blabla\n";

  // Full R-value crazinesss on /tmp/third.
  WriteFooterAndClose(
      WriteSomethingAndGiveItBack(FileOverwriter("/tmp/third")
                                  << "first line\n"
                                  << "second line\n")
      << "one more line"
      << "penultimate line; the end is near\n");

  // To also check the const-reference case (on /tmp/forth)...
  FileOverwriter r("/tmp/forth");
  JustWriteBlahBlah(r);
  // And we don't std::move `r` anywhere; let it die naturally.

  return 0;
} catch (int err) {
  std::cout << strerror(err) << std::endl;
  return 1;
}

Tohle^^^ "obaluje" soubor otevřený POSIXovým stylem pro zápis a zavře ho to jenom jednou, i přes různé předávání do/z funkcí a operátorů na všechny možné způsoby a strany.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
26.8. 17:47 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Pro (některé) anonymy musím ještě zdůraznit, nejlépe desetkrát, že std::ofstream jsem nepoužil jednoduše proto, že zadání znělo "jak obalovat věci z C v C++", nikoliv "jak zapsat soubor v C++". A soubor se zápisem je prostě jenom náhodný příklad; mohlo to být cokoliv jiného, co se v POSIXu dá alokovat.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
26.8. 18:25 unicorncollege
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
hnuz.
26.8. 19:00 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Hnusný anonym.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
26.8. 20:37 unicorncollege
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
videl jsi se tlamo xd xd
27.8. 10:42 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Přesně kvůli ubohým existencím jako ty je ABCLinuxu o dost horší, než by mohlo být.

ABCLinuxu, bylo by možné už konečně systematicky zakázat takováto anonymní hovna???

Ano, bylo! Protože když nedávno jedno z těchto hoven zaplevelilo všechny poradny a diskuse, najednou bylo možné změnit pár zdrojáků a naimplementovat captchu v podstatě za víkend. Dokonce i pár trestních oznámení tam viselo ve vzduchu — až jsem si tenkrát otevřel popcorn ke sledování, co bude dál.

Bezva. No a nešlo tenkrát už konečně odstranit anonymní přístup úplně? Šlo. Proč zůstal zachovaný, to fakt nechápu.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
27.8. 10:50 unicorncollege
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
fňuky fňuk bůůů bůů když to tady nefunguje, tak vypadni, právě jsi o tom psal tak jdi přýkladem xd
27.8. 10:52 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Jďy už přykladem, hovynko anonymňy.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
27.8. 10:55 unicorncollege
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
abicko je opravdu lihen narcisu a autistu fascinuje me jak se ctyrictelitey clovek muze tak posrat s par pismenek na internetu jdu si taky pro ten popkron
27.8. 12:52 chocholousek
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
je zajimave jak trolove se vetsinou projevuji tim ze svoje negativni pocity a problemy projektuji do ostatnich a pak je za ne jakoby trestaji. Asi obecna vlastnost trolu

Prosim krmte ho dale , toto je pro muj obor velmi zajimave.
27.8. 13:05 unicorncollege
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++
takze podle tvy logiky si chtel byt doktorem a nakonec te stve ze si skoncil u lopaty to je mi lito xd akorat nevim koho tim trestas asi sebe a tvoje rodice
xkucf03 avatar 27.8. 10:53 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše anonymní příspěvky v diskusích

Tak ho nekrm a je to.

Zakázat přispívání všem nepřihlášeným kvůli pár idiotům mi přijde hloupé.

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
27.8. 14:20 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: anonymní příspěvky v diskusích

Tak ať on nekrmí mě! Taky jsem troll, nemůžu si pomoct.

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ
xkucf03 avatar 26.8. 19:56 xkucf03 | skóre: 48 | blog: xkucf03
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

To přetěžování operátorů je dobrý nápad, vím, že se s tím dají dělat hezké věci… ale ještě jsem na to neměl odvahu :-)

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
27.8. 10:51 Andrej | skóre: 47 | blog: Republic of Mordor | Zürich
Rozbalit Rozbalit vše Re: Návrhový vzor pro obalování C knihoven v C++

Přetěžování operátorů může být hezký i ošklivý nápad a u tohoto příkladu nemá v podstatě na nic vliv. Místo operátor<<(...) se to klidně mohlo jmenovat write_somethig(...).

Jediné, co write_something(...).write_something(...) nedokáže přesně napodobit, je operátor asociativní zprava. :-) Ale to není případ <<. (Háček celého přetěžovaní je taky v tom, že sice (částečně) ovlivní význam operátorů, ale nic nezmůže stran jejich priority a směru vyhodnocování. Tedy, alespoň ne v C++.)

ǑǦŹǓǕǙǞǺǨȞȬḔḦḰḾṊṎṸẄẌỖ

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267   www.czech-server.cz
© 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.