C++ Bindovanie funkcií, std::funnction a pointery na funkciu

Dobrý deň nedávno som tu založil túto tému: https://forum.root.cz/index.php?topic=23520.0

V skratke do funkcie, ktorá vyžaduje ako parameter pointer na funkciu, s jediným parametrom by som potreboval pridať ďalší parameter. (pozn: Keďže v C++ sa currying nedá použiť (nie tak ako inde) lebo lambda s captures je typovo nekompatibilná s lambdou bez captures takže postupy, ktoré poznám z funkcionálnych jazykov som nemohol použiť)

Spomínali ste, že sa mám pozrieť na type erasure. Niekto tam pridal príklad s constexpr ktorý síce fungoval ale len s primitívnymi typmi. A keď som chcel ten primitívny typ nahradiť objektom tak compiler protestoval.

Hral som sa aj s tými type erasures, ale nejako mi to nefungovalo.V čistom C som nikdy neprogramoval a v C++ som začiatočník venujem sa mu občas s niekoľko mesačnými prestávkami. Takže Cčkovský pointer na funkciu je pre mňa niečo nové. C++ má typ std::function s ktorým mi všetko bez problémov funguje (kôli vačšej prehľadnosti kódu som to hodil na pastebin):

https://pastebin.com/zEY0H7Ay - verzia std::function mi funguje.

Lenže ja mám windows api funkciu ktorá ako parameter nepýta std::function z C++ ale pointer na funkciu. To znamená že som použil typ std::function naň som aplikoval std::bind pridal som parameter. A potom som sa snažil to prekonvertovať na Cčkový pointer na funkciu:

https://pastebin.com/5icv1F7b - verzia s konverziou na pointer na funkciu mi nefunguje. Skompilovať ide ale potom mi to hodí runtime error:

Exception thrown at 0x0000000000000000 in Erasure.exe: 0xC0000005: Access violation executing location 0x0000000000000000. Niečo tam mám evidentne zle ale ani za svet nedokážem prísť na to že čo.

Viete mi prosím poradiť ako nabindovaný std::function<int(int)> prekonvertovať na int(*)(int item)?

PS: Tá funkcia map je len príklad... Samoztejme viem že C++ má std::transform používajúce iterátory. Mne išlo len o ukážku kódu. V mojom projekte riešim niečo iné, ale súvisí to tiež s bindovaním funkcií. Akurát tam nechcem nabindovať premennú primitívneho typu, ale objekt.


Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #1 kdy: 26. 09. 2020, 16:26:39 »
Ach jaj ten druhý odkaz je ten istý čo prvý... opravujem to... malo tam byť toto: https://pastebin.com/7FFks1y1

Kód: [Vybrat]
#include <iostream>
#include <vector>
#include <functional>
 
template<typename T>
void dump(std::vector<T> keys)
{
    std::wcout << L"Keys: [";
    auto isFirst = true;
    for (auto key : keys)
    {
        std::wcout << (isFirst ? L"" : L", ") << key;
        isFirst = false;
    }
    std::wcout << L"]" << std::endl;
}
 
template<typename T1, typename T2> using Mapper = T2(*)(T1 item);
 
template<typename T1, typename T2>
std::vector<T2> map(const Mapper<T1, T2> mapper, const std::vector<T1> vec)
{
    std::vector<T2> result;
    for (T1 x : vec)
    {
        result.push_back(mapper(x));
    }
    return result;
}
 
using namespace std::placeholders;
auto func = [](int n, int m)
{
    return n * m;
};
 
int main()
{
    auto multiplier = 5;
    std::function<int(int)> boundMapper = std::bind(func, _1, multiplier);
    auto mapper = *boundMapper.target<int(int)>();
    auto list = map<int, int>(
        mapper,
        { 1, 2, 3 }
    );
    dump<int>(list);
}

tecka

Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #2 kdy: 26. 09. 2020, 19:43:35 »
Ano, target ti vrátí nullptr. Ze std::function se function-pointery nevyrábějí. Pokud ho potřebuješ, tak použij klasickou funkci.

Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #3 kdy: 26. 09. 2020, 22:34:16 »
Ano, target ti vrátí nullptr. Ze std::function se function-pointery nevyrábějí. Pokud ho potřebuješ, tak použij klasickou funkci.

Ahoj a nevedel by si mi prosím poradiť ako na miesto kde sa očakáva typ:

Kód: [Vybrat]
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
vložiť (bindnúť alebo také niečo) ešte jeden parameter? Potrebujem tam dostať inštanciu triedy ktorá WNDPROC obaluje.  Keďže sa tam vyžaduje Cčkový pointer na funkciu nemôžem tam použiť std::bind (resp ani boost::bind)

Momentálne to riešim cez static. Zatiaľ mi to stačí, ale chcel by som mať možnosť vytvoriť viac inštancíi a každú predať ako parameter do WNDPROC (WNDPROC musí byť static).

Žiaľ toto je asi prvý vážnejší zákys v C++ na, ktorý nie a nie nájsť riešenie. A možno je to riešenie aj veľmi jednoduché, len mi chýba nejaká podstatná znalosť. Preto sa to snažím vyriešiť štandartným spôsobom. Viem, že by sa dala vytvoriť std::map a v nej indexovať jednotlivé inštancie podľa HWND, ale ja sa chcem naučiť ako sa to robí normálnym spôsobom, nie cez nejaké obskurné hacky.

Idris

  • *****
  • 947
    • Zobrazit profil
    • E-mail
Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #4 kdy: 26. 09. 2020, 23:41:30 »
Ano, target ti vrátí nullptr. Ze std::function se function-pointery nevyrábějí. Pokud ho potřebuješ, tak použij klasickou funkci.

Ahoj a nevedel by si mi prosím poradiť ako na miesto kde sa očakáva typ:

Kód: [Vybrat]
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
vložiť (bindnúť alebo také niečo) ešte jeden parameter? Potrebujem tam dostať inštanciu triedy ktorá WNDPROC obaluje.  Keďže sa tam vyžaduje Cčkový pointer na funkciu nemôžem tam použiť std::bind (resp ani boost::bind)

Momentálne to riešim cez static. Zatiaľ mi to stačí, ale chcel by som mať možnosť vytvoriť viac inštancíi a každú predať ako parameter do WNDPROC (WNDPROC musí byť static).

Žiaľ toto je asi prvý vážnejší zákys v C++ na, ktorý nie a nie nájsť riešenie. A možno je to riešenie aj veľmi jednoduché, len mi chýba nejaká podstatná znalosť. Preto sa to snažím vyriešiť štandartným spôsobom. Viem, že by sa dala vytvoriť std::map a v nej indexovať jednotlivé inštancie podľa HWND, ale ja sa chcem naučiť ako sa to robí normálnym spôsobom, nie cez nejaké obskurné hacky.
RegisterClass má parametr pro alokaci paměti navíc nad rámec windowsí struktury. Tam se dají předat uživatelská data. Spíše než přímo std::function bych tam dal ukazatel na funktor, ať se snáze spravuje paměť. Takový funktor pak může obsahovat std::function nebo mít kód přímo v přetíženém operátoru ().


Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #5 kdy: 27. 09. 2020, 01:50:01 »
Ano, target ti vrátí nullptr. Ze std::function se function-pointery nevyrábějí. Pokud ho potřebuješ, tak použij klasickou funkci.

Ahoj a nevedel by si mi prosím poradiť ako na miesto kde sa očakáva typ:

Kód: [Vybrat]
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
vložiť (bindnúť alebo také niečo) ešte jeden parameter? Potrebujem tam dostať inštanciu triedy ktorá WNDPROC obaluje.  Keďže sa tam vyžaduje Cčkový pointer na funkciu nemôžem tam použiť std::bind (resp ani boost::bind)

Momentálne to riešim cez static. Zatiaľ mi to stačí, ale chcel by som mať možnosť vytvoriť viac inštancíi a každú predať ako parameter do WNDPROC (WNDPROC musí byť static).

Žiaľ toto je asi prvý vážnejší zákys v C++ na, ktorý nie a nie nájsť riešenie. A možno je to riešenie aj veľmi jednoduché, len mi chýba nejaká podstatná znalosť. Preto sa to snažím vyriešiť štandartným spôsobom. Viem, že by sa dala vytvoriť std::map a v nej indexovať jednotlivé inštancie podľa HWND, ale ja sa chcem naučiť ako sa to robí normálnym spôsobom, nie cez nejaké obskurné hacky.
RegisterClass má parametr pro alokaci paměti navíc nad rámec windowsí struktury. Tam se dají předat uživatelská data. Spíše než přímo std::function bych tam dal ukazatel na funktor, ať se snáze spravuje paměť. Takový funktor pak může obsahovat std::function nebo mít kód přímo v přetíženém operátoru ().

Tak to sme sa asi nepochopili.

Keby sa dal do štruktúry určenej pre funckiu RegisterClassEx (WNDCLASSEXW) vložiť typ std::function tak mám po probléme, std::function je veľmi flexibilný a viem ho nabindovať, ale ten už potom neviem skonvertovať na typ WNDPROC. S C++ funckionálny typom nemám najemnší problém žiaľ tento typ neni kompatibilný s Cčkovým pointerom na funkciu. std::function sa tam vložiť žiaľ nedá (alebo rbím niečo zle???) ak by ste poznali spôsob ako do typu WNDPROC vložiť std::function alebo pointer na funktor tak sem s ním - budem len rád. Ja mám, ale pocit že ani jedno ani druhé nejde. Lebo googlil som o tom dnes niekoľko hodín skúšal som všetky možné spôsoby a nič relevantné som nenašiel. Práca s C typmi je značne obmedzená. C++ typy sú o dosť vpredu, ale keďže WinAPI je napísané v C a s konverziami sú problémy, tak si moc nepomôžem.

Ak by ste, ale vedeli ako tam dostať std::function alebo funkctor tak Vás poprosím o príklad. Možno že na to idem zle.

Kód: [Vybrat]
auto szWindowClass = L"ClipboardListener";
auto wndClass = WNDCLASSEXW{};
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.lpfnWndProc = ClipboardListener::WndProc; //staticka metoda
wndClass.hInstance = GetModuleHandle(nullptr);
wndClass.lpszClassName = szWindowClass;
if (!RegisterClassExW(&wndClass))

 skúsim tam vložiť custom dáta. tak ako ste mi radili ale tiež si myslím že ten functor by bolo čistejšie riešenie (a viem si tam pribaliť hocičo) takže ak by to bolo predsa len možné tiež by som ho prefereoval.

POZN typ WNDPROC: typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);

Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #6 kdy: 27. 09. 2020, 02:06:37 »
Jo a to pribalovanie dát som si práve vygooglil:

https://stackoverflow.com/questions/18161680/how-do-i-stdbind-a-non-static-class-member-to-a-win32-callback-function-wnd#18162974

Na tento konkrétny prípad sa to hodí.

Ale môžu nastať aj iné podobné situácie kde sa to už hodiť nemusí. (Napr pri hookoch LowLevelKeyboardProc) Preto som hľadal čo najvšeobecnejšie riešenie. Prvé ma napadlo použiť labdu s captures, lenže tá neni kompatibilná s pointerom na funkciu. ďalej som skúšal použiť std::bind (a tiež std::mem_fun), tie fungovali, ale tiež neboli kompatibilné s C-čkovým pointerom na funkciu ale len s C++osvým std::function.

Takže pre C-čkový pointer na funkciu všeobecné riešenie asi ani neexistuje (mimo použitia static). Ja sa snažím čo najviac využívať moderné konštrukcie z C++ ale pri RegisterClass to žiaľ nejde. Ale aspoň som sa dnes niečo nové naučil aj o C-čkových typoch.
« Poslední změna: 27. 09. 2020, 02:11:22 od fortran1986 »

Idris

  • *****
  • 947
    • Zobrazit profil
    • E-mail
Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #7 kdy: 27. 09. 2020, 09:48:26 »
Keby sa dal do štruktúry určenej pre funckiu RegisterClassEx (WNDCLASSEXW) vložiť typ std::function tak mám po probléme, std::function je veľmi flexibilný a viem ho nabindovať, ale ten už potom neviem skonvertovať na typ WNDPROC.
To samozřejmě nejde, musí se předat čistá céčková funkce, která si pak vezme ten ukazatel na funktor a zavolá jej. To už jde snadno. Volaný objekt si prostě žije na haldě včetně kontextu a ten callback si musí ukazatel přetypovat a zavolat.

Idris

  • *****
  • 947
    • Zobrazit profil
    • E-mail
Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #8 kdy: 27. 09. 2020, 10:00:29 »
Jo a to pribalovanie dát som si práve vygooglil:

https://stackoverflow.com/questions/18161680/how-do-i-stdbind-a-non-static-class-member-to-a-win32-callback-function-wnd#18162974

Na tento konkrétny prípad sa to hodí.

Ale môžu nastať aj iné podobné situácie kde sa to už hodiť nemusí. (Napr pri hookoch LowLevelKeyboardProc) Preto som hľadal čo najvšeobecnejšie riešenie. Prvé ma napadlo použiť labdu s captures, lenže tá neni kompatibilná s pointerom na funkciu. ďalej som skúšal použiť std::bind (a tiež std::mem_fun), tie fungovali, ale tiež neboli kompatibilné s C-čkovým pointerom na funkciu ale len s C++osvým std::function.

Takže pre C-čkový pointer na funkciu všeobecné riešenie asi ani neexistuje (mimo použitia static). Ja sa snažím čo najviac využívať moderné konštrukcie z C++ ale pri RegisterClass to žiaľ nejde. Ale aspoň som sa dnes niečo nové naučil aj o C-čkových typoch.
Ono to nejde z principu, uzávěry mají kontext. Proto mají vždy callbacky v C parametr pro uživatelská data (typicky ukazatel). Pro tu klávesnici je ve WinAPI taky, v KBDLLHOOKSTRUCT.

Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #9 kdy: 27. 09. 2020, 13:20:58 »
Citace
Niečo tam mám evidentne zle ale ani za svet nedokážem prísť na to že čo.

nóó budu citovat mistra  ;D ;D ;D ;D ;) ;)
Citace: Jendа, https://www.abclinuxu.cz/poradna/programovani/show/459486#9
Doporučuju používat nějaký hustý kompilátor (třeba rozumně nové gcc) se zapnutými všemi warningy (tohle bylo -Wall -Wextra -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wrestrict -Wnull-dereference -Wjump-misses-init -Wdouble-promotion -Wshadow -Wformat=2)
takže třeba s hustým g++ 10 to dopadne takle nějak a už je aspoň jakoby čeho se chytit ;) ;)
Kód: [Vybrat]
g++-10 -Wall -Wextra -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wrestrict -Wnull-dereference -Wdouble-promotion -Wshadow -Wformat=2 fortranuvZdrojacek.cpp
In file included from /usr/include/c++/10/functional:59,
                 from fortranuvZdrojacek.cpp:3:
/usr/include/c++/10/bits/std_function.h: In instantiation of ‘_Functor* std::function<_Res(_ArgTypes ...)>::target() [with _Functor = int(int); _Res = int; _ArgTypes = {int}]’:
fortranuvZdrojacek.cpp:42:49:   required from here
/usr/include/c++/10/bits/std_function.h:649:9: error: invalid use of ‘const_cast’ with type ‘int (*)(int)’, which is a pointer or reference to a function type
  649 |  return const_cast<_Functor*>(__func);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

zkrátka žádej legální software kvalitní kompilátor misto tamtoho nějakýho divnýho microsoft visual studia. a to i na zdrojáčky který nepišeš pod linuxem ale pod win ;D ;)

Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #10 kdy: 27. 09. 2020, 16:36:25 »
Mrkni na SetWindowLongPtr() - pomocí této funkce si můžeš asociovat data s HWND, v callbacku si je vytáhnout, a pak je použít.

Re:C++ Bindovanie funkcií, std::funnction a pointery na funkciu
« Odpověď #11 kdy: 27. 09. 2020, 21:20:12 »
...

Ďakujem za vysvetlenie. Aj som si aj intuitívne myslel, že je to kôli tomu.

Mrkni na SetWindowLongPtr() - pomocí této funkce si můžeš asociovat data s HWND, v callbacku si je vytáhnout, a pak je použít.

Hej našiel som to na tom odkaze zo stackoverflow, čo som sem včera linkoval,  už to mám aj naprogramované, ale ajtak dik.

Citace
Niečo tam mám evidentne zle ale ani za svet nedokážem prísť na to že čo.

nóó budu citovat mistra  ;D ;D ;D ;D ;) ;)
Citace: Jendа, https://www.abclinuxu.cz/poradna/programovani/show/459486#9
Doporučuju používat nějaký hustý kompilátor (třeba rozumně nové gcc) se zapnutými všemi warningy (tohle bylo -Wall -Wextra -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wrestrict -Wnull-dereference -Wjump-misses-init -Wdouble-promotion -Wshadow -Wformat=2)
takže třeba s hustým g++ 10 to dopadne takle nějak a už je aspoň jakoby čeho se chytit ;) ;)
Kód: [Vybrat]
g++-10 -Wall -Wextra -Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wrestrict -Wnull-dereference -Wdouble-promotion -Wshadow -Wformat=2 fortranuvZdrojacek.cpp
In file included from /usr/include/c++/10/functional:59,
                 from fortranuvZdrojacek.cpp:3:
/usr/include/c++/10/bits/std_function.h: In instantiation of ‘_Functor* std::function<_Res(_ArgTypes ...)>::target() [with _Functor = int(int); _Res = int; _ArgTypes = {int}]’:
fortranuvZdrojacek.cpp:42:49:   required from here
/usr/include/c++/10/bits/std_function.h:649:9: error: invalid use of ‘const_cast’ with type ‘int (*)(int)’, which is a pointer or reference to a function type
  649 |  return const_cast<_Functor*>(__func);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~

zkrátka žádej legální software kvalitní kompilátor misto tamtoho nějakýho divnýho microsoft visual studia. a to i na zdrojáčky který nepišeš pod linuxem ale pod win ;D ;)


Ďakujem za tip. G++ si chcem rozbehať priamo vo Visual Studiu 2019. Ale momentálne používam MSVC compiler a Clang, lebo tie má Visualko by default. MSVC má fakt strašne neadresné chybové hlásenia. Ale keď som použil CLang, tak na tom bol možno ešte horšie. GCC/G++ používam keď kompilujem nejakú statickú libku tretej strany, ktorá používa makefile, kompilujem to pod WSL(ubuntu) a potom si do MSVC projektu, len pridám cestu k includes. Takže vlastne používam všetky 3 compilery. Ale čo sa velkosti výstupu týka, tak naozaj to MSVC je katastrofa pred chvíľkou som kompiloval jednu libku - boost::di (IoC kontainer pre C++) a stačí porovnať rýchlosť kompilácie a veľkosť výstupu všetkých troch compilerov https://github.com/boost-ext/di#:~:text=Run%20this%20example%20on%20Wandbox. Microsoft svoj kompiler úplne odflákol.

Btw plánujem tú appku prepísať aj do linuxu, ale niektoré časti ktoré sú naviazané na OS bude treba prepísať úplne celé, ale celkom sa na to teším aspoň sa niečo nové naučím, preto som ani nepoužil Qt ktoré obsahovalo to čo vyvýjam.

Každopádne začal som Windowsom pretože s programovaním pre Windows mám najviac skúseností a potom prejdem na linux.

A keď už ste spoemnuli to GCC, ktoré IDEčko používate na C++ vývoj prod linuxom? Môže byť kludne aj komerčné. A neviete o nejakom IDE, ktoré zvýrazňuje korektne aj moduly z C++ 20?  Lebo MSVC compiler síce moduly podporuje a dokáže projekt obsahujúci moduly skompilovať (dnes som to skúšal). Ale editor IDEčka sa správa ako keby žiadne moduly v C++ neexistovali, pritom som ich zapol aj v konfigurácii projektu (Enable C++ Modules: Yes (/experimental:module)) ale na editor to nemalo absolútne žiaden vplyv.