Witam. Mam załóżmy sobie klasę, w której muszę użyć funkcji zdefiniowanej przez użytkownika mającą postać int(string, string)
Jak zrobić wskaźnik do funkcji, aby można było podać nazwę funkcji jak i metody ?
Co to znaczy podać nazwę funkcji jak i metody
?
Pokaż konkretny przykład co chciałbyś uzyskać.
Jeżeli ktoś będzie chciał użyć metody, niech użyje std::bind
lub lambd. Lub obu.
typedef int( * GetLengthFunc )( void );
class Klasa{
public:
Klasa(GetLengthFunc newFunc);
GetLengthFunc GetLength;
};
Klasa::Klasa(GetLengthFunc newFunc){
GetLength = newFunc;
}
/// funkcja zadziała prawidłowo jako argument kontruktora
int funkcja(){return 100;}
/// metoda nie zadziala prawidlowo
class myClass{
public:
int metoda(){return 100;}
}
jak zrobić, abym zarówno metody jak i funkcje mógł przekazywać do konstruktora?
Możesz użyć std::function + std::bind / lambda:
https://oopscenities.net/2012/02/24/c11-stdfunction-and-stdbind/
Możesz użyć zwykłego szablonu (który przyjmie wszystko jak leci - funktor, funkcję, lambdę).
std::bind możesz użyć do konwersji metody na funktor: http://en.cppreference.com/w/cpp/utility/functional/bind
(szukaj print_sum)
Dobra to mam lekturkę, dozobaczenia wszystkim xd już 5 raz przymierzam się do lambd i przegrywam. Ale widze nie mam wyboru.
Ehh, nie bardzo rozumiem chyba te lambdy. Funkcja int(void) odpowiada typowi przekazanemu do kontruktora klasy. Przekazuję adres funkcji.
Macie pomysły, co jest źle? Proszę tylko o naprostowanie mojego toku myślenia ^^
KOD na którym to sprawdzam:
#include <iostream>
using namespace std;
typedef int( * FUNCGETSIZE )( void );
class KLASA1{
public:
KLASA1(FUNCGETSIZE newFoo);
FUNCGETSIZE Foo;
void SetA();
private:
int a;
};
KLASA1::KLASA1(FUNCGETSIZE newFoo){
Foo = newFoo;
}
void KLASA1::SetA(){
a = Foo();
cout << a << endl;
}
class KLASA2{
public:
KLASA2();
int GetSize();
void PlusA();
private:
int a;
};
KLASA2::KLASA2(){
a = 0;
}
int KLASA2::GetSize(){
return a;
}
void KLASA2::PlusA(){
++a;
}
int main()
{///
class KLASA2 *k2 = new KLASA2();
class KLASA1 *k1 = new KLASA1(
[](void)->int{ return k2.GetSize(); }
);
k1->SetA();
k2->PlusA();
k1->SetA();
k2->PlusA();
k1->SetA();
k2->PlusA();
k1->SetA();
k2->PlusA();
delete k1;
delete k2;
return 0;
}
ERROR:
C:\Users\Świdwa\Desktop\Untitled1.cpp|45|error: 'k2' is not captured|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
Lambda ma pusty capture-list, a próbujesz użyć k2
, które ani nie jest parametrem, ani elementem capture-list.
Czyli według http://www.umich.edu/~eecs381/handouts/Lambda.pdf
mogę zrobić tak?
class KLASA2 *k2 = new KLASA2();
auto it = [k2](void)->int{ return k2->GetSize();};
class KLASA1 *k1 = new KLASA1(
it
);
k1->SetA();
k2->PlusA();
teraz nie pasuje mi typ przekazywany do konstruktora :(
C:\Users\Świdwa\Desktop\Untitled1.cpp||In function 'int main()':|
C:\Users\Świdwa\Desktop\Untitled1.cpp|45|error: no matching function for call to 'KLASA1::KLASA1(main()::__lambda0&)'|
C:\Users\Świdwa\Desktop\Untitled1.cpp|45|note: candidates are:|
C:\Users\Świdwa\Desktop\Untitled1.cpp|13|note: KLASA1::KLASA1(FUNCGETSIZE)|
C:\Users\Świdwa\Desktop\Untitled1.cpp|13|note: no known conversion for argument 1 from 'main()::__lambda0' to 'FUNCGETSIZE {aka int (*)()}'|
C:\Users\Świdwa\Desktop\Untitled1.cpp|5|note: constexpr KLASA1::KLASA1(const KLASA1&)|
C:\Users\Świdwa\Desktop\Untitled1.cpp|5|note: no known conversion for argument 1 from 'main()::__lambda0' to 'const KLASA1&'|
C:\Users\Świdwa\Desktop\Untitled1.cpp|5|note: constexpr KLASA1::KLASA1(KLASA1&&)|
C:\Users\Świdwa\Desktop\Untitled1.cpp|5|note: no known conversion for argument 1 from 'main()::__lambda0' to 'KLASA1&&'|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
Z pewnością
typedef int( * FUNCGETSIZE )( void );
nie pasuje do:auto it = k2->int{ return k2->GetSize();};
Ale jak temu zaradzić?
Tylko lambdy z pustymi capture-list można rzutować na wskaźnik na funkcję. Jak chcesz catch-all to musisz użyć std::function
. Przy okazji, używaj czytelniejszego syntaxu C++11:
using FUNCGETSIZE = int(*)(void);
Zamień:
typedef int(*FUNCGETSIZE)(void);
na:
typedef std::function<int(void)> FUNCGETSIZE;
To w takim razie powinno to wyglądać chyba tak:
using FUNCGETSIZE = std::function<int(void)>;
function<int(void)> FOO = k2->GetSize;
class KLASA1 *k1 = new KLASA1(
FOO
);
BŁEDY:
C:\Users\Świdwa\Desktop\Untitled1.cpp||In function 'int main()':|
C:\Users\Świdwa\Desktop\Untitled1.cpp|46|error: conversion from '<unresolved overloaded="overloaded" function="function" type="type">' to non-scalar type 'std::function<int()>' requested|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
Niestety. Kompletnie tego nie czaję. Postaram sie jutro poczytać jeszcze jakieś artykuły może coś mi wpadnie do głowy i się odezwę jak nie dam rady.
AAAAAAAA MAM !
Połączyłem function z lambdą i działa !!!!!!!!!
function<int(void)> FOO = [k2](void)->int{ return k2->GetSize();};
class KLASA1 *k1 = new KLASA1( FOO );
Bardzo Wam dziekuję chłopaki za pomoc. Mogę iść w spokoju spać :)
Ostatnie pytanie: Skad lambda wie, jaka wartość zwrócić?
function<int(void)> FOO = [k2] { return k2->GetSize(); };
Panowie mam kolejną zagwostkę:
mam procedurę zadeklarowana w klasie:
LRESULT CALLBACK SEARCHEDIT::SubEditProc( HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam )
i muszę dokonać subclassingu Edita.
Zrobiłem coś takiego:
auto it = [this](HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam)->LRESULT CALLBACK{ return 0;};
function<LRESULT CALLBACK(HWND, UINT, WPARAM, LPARAM)> SubFunc = it;
DefaultProc = ( WNDPROC ) SetWindowLongPtr( hEdit, GWLP_WNDPROC,( LONG ) it );
||=== Build: Release in WH_GDI (compiler: GNU GCC Compiler) ===|
C:\Users\Świdwa\Desktop\Programy\WH_GDI\wh_SearchEdit.cpp||In member function 'bool SEARCHEDIT::Create(int, int, int, int, HWND, int)':|
C:\Users\Świdwa\Desktop\Programy\WH_GDI\wh_SearchEdit.cpp|75|error: variable 'std::function<long int(HWND__*, unsigned int, unsigned int, long int)> SubFunc' has initializer but incomplete type|
C:\Users\Świdwa\Desktop\Programy\WH_GDI\wh_SearchEdit.cpp|77|error: invalid cast from type 'SEARCHEDIT::Create(int, int, int, int, HWND, int)::__lambda0' to type 'LONG {aka long int}'|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 1 second(s)) ===|
Ogólnie
auto it = [this](HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam)->LRESULT CALLBACK{ return 0;};
jest dobrze zrobione, ale dalej nie potrafię wsadzić tego do funkcji SetWindowPtr(). Ponad to, zależy mi na wsadzeniu tam nie lambdy, tylko metody: LRESULT CALLBACK SEARCHEDIT::SubEditProc( HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam )
Mogę prosić o drobną wskazówkę?
CALLBACK
to calling convention, nie wiem jak to w lambdzie zadeklarować, standard tego nie przewiduje. Rozwiązanie dla msvc: http://stackoverflow.com/questions/18367641/use-createthread-with-a-lambda
A znasz inne rozwiązanie jak sprawić, aby metoda była widziana jako zwykła funkcja?
Metoda to nie jest zwykła funkcja, nie może być tak widziana (musi dostać niejawne this
)
Wygląda na to że próbujesz skonwertować std::function (a raczej lambdę) na LONG, mam wątpliwości czy to jest takie proste.
Zajrzyj tutaj:
http://meh.schizofreni.co/programming/magic/2013/01/23/function-pointer-from-lambda.html