Funkcja wywołania zwrotnego w C++

Callback Function C



Funkcja wywołania zwrotnego to funkcja będąca argumentem, a nie parametrem w innej funkcji. Drugą funkcję można nazwać funkcją główną. W grę wchodzą więc dwie funkcje: główna funkcja i sama funkcja wywołania zwrotnego. Na liście parametrów funkcji principal znajduje się deklaracja funkcji zwrotnej bez jej definicji, podobnie jak deklaracje obiektów bez przypisania. Główna funkcja jest wywoływana z argumentami (w main()). Jednym z argumentów w wywołaniu funkcji głównej jest efektywna definicja funkcji zwrotnej. W C++ ten argument jest odwołaniem do definicji funkcji zwrotnej; to nie jest faktyczna definicja. Sama funkcja zwrotna jest wywoływana w definicji funkcji głównej.

Podstawowa funkcja wywołania zwrotnego w C++ nie gwarantuje asynchronicznego zachowania w programie. Prawdziwą korzyścią ze schematu funkcji wywołania zwrotnego jest zachowanie asynchroniczne. W schemacie funkcji asynchronicznego wywołania zwrotnego wynik funkcji głównej powinien zostać uzyskany dla programu przed uzyskaniem wyniku funkcji wywołania zwrotnego. Można to zrobić w C++; jednak C++ ma bibliotekę o nazwie future, która gwarantuje zachowanie schematu asynchronicznej funkcji wywołania zwrotnego.







W tym artykule wyjaśniono podstawowy schemat funkcji wywołania zwrotnego. Wiele z tego jest w czystym C++. Jeśli chodzi o callback, wyjaśnione jest również podstawowe zachowanie przyszłej biblioteki. Do zrozumienia tego artykułu niezbędna jest podstawowa znajomość języka C++ i jego wskaźników.



Treść artykułu

Podstawowy schemat funkcji oddzwaniania

Schemat funkcji wywołania zwrotnego wymaga funkcji głównej i samej funkcji wywołania zwrotnego. Deklaracja funkcji zwrotnej jest częścią listy parametrów funkcji głównej. Definicja funkcji zwrotnej jest wskazana w wywołaniu funkcji funkcji głównej. Funkcja wywołania zwrotnego jest faktycznie wywoływana w definicji funkcji głównej. Poniższy program ilustruje to:



#włączać

za pomocą przestrzeń nazwgodziny;



intmainFn(zwęglaćch[],int (*ptr)(int))

{

intid1= 1;

intid2= 2;

intzwykle= (*ptr)(id2);

koszt<<'główna funkcja: '<<id1<<''<<ch<<''<<zwykle<<' ';

powrótid1;

}


intcb(inttożsamość)

{

koszt<<„funkcja oddzwaniania”<<' ';

powróttożsamość;

}


intGłówny()

{

int (*ptr)(int) = &cb;

zwęglaćnie[] = 'oraz';

mainFn(ojciec, cb);



powrót 0;

}

Dane wyjściowe to:





funkcja oddzwaniania

główna funkcja: 1 oraz 2

Funkcja Principal jest identyfikowana przez principalFn(). Funkcja zwrotna jest identyfikowana przez cb(). Funkcja wywołania zwrotnego jest zdefiniowana poza funkcją Principal, ale faktycznie wywoływana w ramach funkcji Principal.

Zwróć uwagę na deklarację funkcji zwrotnej jako parametr na liście parametrów deklaracji funkcji głównej. Deklaracja funkcji zwrotnej to int (*ptr)(int). Zwróć uwagę na wyrażenie funkcji wywołania zwrotnego, podobnie jak wywołanie funkcji, w definicji funkcji głównej; każdy argument wywołania funkcji zwrotnej jest tam przekazywany. Instrukcja dla tego wywołania funkcji to:



intzwykle= (*ptr)(id2);

Gdzie id2 jest argumentem. ptr jest częścią parametru, wskaźnikiem, który będzie połączony z referencją funkcji zwrotnej w funkcji main().

Zwróć uwagę na wyrażenie:

int (*ptr)(int) = &cb;

W funkcji main(), która łączy deklarację (bez definicji) funkcji zwrotnej z nazwą definicji tej samej funkcji zwrotnej.

Główna funkcja jest wywoływana w funkcji main() jako:

mainFn(ojciec, cb);

Gdzie cha jest napisem, a cb jest nazwą funkcji zwrotnej bez żadnego jej argumentu.

Synchroniczne zachowanie funkcji wywołania zwrotnego

Rozważ następujący program:

#włączać

za pomocą przestrzeń nazwgodziny;



próżniamainFn(próżnia (*ptr)())

{

koszt<<„główna funkcja”<<' ';

(*ptr)();

}


próżniacb()

{

koszt<<„funkcja oddzwaniania”<<' ';

}


próżniafn()

{

koszt<<'widziany'<<' ';

}


intGłówny()

{

próżnia (*ptr)() = &cb;

mainFn(cb);

fn();



powrót 0;

}

Dane wyjściowe to:

główna funkcja

funkcja oddzwaniania

widziany

Tutaj jest nowa funkcja. Wszystko, co robi nowa funkcja, to wyświetlanie danych wyjściowych. W funkcji main() wywoływana jest funkcja główna, a następnie wywoływana jest nowa funkcja fn(). Wynik pokazuje, że kod dla funkcji głównej został wykonany, następnie dla funkcji zwrotnej, a na końcu dla funkcji fn(). Jest to zachowanie synchroniczne (jednowątkowe).

Gdyby było to zachowanie asynchroniczne, gdy trzy segmenty kodu są wywoływane w kolejności, pierwszy segment kodu może zostać wykonany, po czym zamiast tego wykonać trzeci segment kodu, przed wykonaniem drugiego segmentu kodu.

Cóż, funkcję fn() można wywołać z definicji funkcji głównej, zamiast z funkcji main(), w następujący sposób:

#włączać

za pomocą przestrzeń nazwgodziny;



próżniafn()

{

koszt<<'widziany'<<' ';

}


próżniamainFn(próżnia (*ptr)())

{

koszt<<„główna funkcja”<<' ';

fn();

(*ptr)();

}


próżniacb()

{

koszt<<„funkcja oddzwaniania”<<' ';

}


intGłówny()

{

próżnia (*ptr)() = &cb;

mainFn(cb);



powrót 0;

}

Dane wyjściowe to:

główna funkcja

widziany

funkcja oddzwaniania

Jest to imitacja zachowania asynchronicznego. To nie jest zachowanie asynchroniczne. Jest to nadal zachowanie synchroniczne.

Również kolejność wykonywania segmentu kodu funkcji głównej i segmentu kodu funkcji zwrotnej można zamienić w definicji funkcji głównej. Poniższy program ilustruje to:

#włączać

za pomocą przestrzeń nazwgodziny;



próżniamainFn(próżnia (*ptr)())

{

(*ptr)();

koszt<<„główna funkcja”<<' ';

}


próżniacb()

{

koszt<<„funkcja oddzwaniania”<<' ';

}


próżniafn()

{

koszt<<'widziany'<<' ';

}


intGłówny()

{

próżnia (*ptr)() = &cb;

mainFn(cb);

fn();



powrót 0;

}

Wyjście jest teraz,

funkcja oddzwaniania

główna funkcja

widziany

Jest to również imitacja zachowania asynchronicznego. To nie jest zachowanie asynchroniczne. Jest to nadal zachowanie synchroniczne. Prawdziwe zachowanie asynchroniczne można uzyskać, jak wyjaśniono w następnej sekcji lub z biblioteką przyszłości.

Zachowanie asynchroniczne z funkcją wywołania zwrotnego

Pseudokod podstawowego schematu funkcji asynchronicznego wywołania zwrotnego to:

typ wyjścia;

wpisz cb(typ wyjścia)

{

//sprawozdania

}


wpisz głównyFn(wpisz wejście, wpisz cb(typ wyjścia))

{

//sprawozdania

}

Zwróć uwagę na pozycje danych wejściowych i wyjściowych w różnych miejscach pseudokodu. Wejście funkcji zwrotnej jest jej wyjściem. Parametry funkcji głównej są parametrem wejściowym kodu ogólnego i parametrem funkcji zwrotnej. W tym schemacie trzecia funkcja może zostać wykonana (wywołana) w funkcji main() przed odczytaniem wyjścia funkcji zwrotnej (nadal w funkcji main()). Poniższy kod ilustruje to:

#włączać

za pomocą przestrzeń nazwgodziny;

zwęglać *wyjście;


próżniacb(zwęglaćna zewnątrz[])

{

wyjście=na zewnątrz;

}



próżniamainFn(zwęglaćWejście[],próżnia (*ptr)(zwęglać[pięćdziesiąt]))

{

(*ptr)(Wejście);

koszt<<„główna funkcja”<<' ';

}


próżniafn()

{

koszt<<'widziany'<<' ';

}


intGłówny()

{

zwęglaćWejście[] = „funkcja oddzwaniania”;

próżnia (*ptr)(zwęglać[]) = &cb;

mainFn(wejście, cb);

fn();

koszt<<wyjście<<' ';



powrót 0;

}

Wyjście programu to:

główna funkcja

widziany

funkcja oddzwaniania

W tym konkretnym kodzie dane wyjściowe i wejściowe są tym samym. Wynik trzeciego wywołania funkcji w funkcji main() został wyświetlony przed wynikiem funkcji zwrotnej. Funkcja wywołania zwrotnego wykonała, zakończyła i przypisała swój wynik (wartość) do zmiennej, wyjście, umożliwiając kontynuowanie programu bez jego ingerencji. W funkcji main() dane wyjściowe funkcji zwrotnej były używane (odczytywane i wyświetlane), gdy było to potrzebne, co prowadziło do asynchronicznego zachowania całego schematu.

Jest to jednowątkowy sposób uzyskania asynchronicznego zachowania funkcji wywołania zwrotnego w czystym C++.

Podstawowe użytkowanie przyszłej Biblioteki

Idea schematu asynchronicznej funkcji wywołania zwrotnego polega na tym, że funkcja główna zwraca się przed powrotem z funkcji wywołania zwrotnego. Zostało to zrobione pośrednio, skutecznie, w powyższym kodzie.

Zauważ z powyższego kodu, że funkcja zwrotna otrzymuje główne dane wejściowe dla kodu i generuje główne dane wyjściowe dla kodu. Przyszłość biblioteki C++ ma funkcję o nazwie sync(). Pierwszym argumentem tej funkcji jest odwołanie do funkcji zwrotnej; drugi argument to dane wejściowe funkcji zwrotnej. Funkcja sync() powraca bez czekania na zakończenie wykonania funkcji zwrotnej, ale umożliwia wykonanie funkcji zwrotnej. Zapewnia to zachowanie asynchroniczne. Podczas gdy funkcja wywołania zwrotnego jest nadal wykonywana, ponieważ funkcja sync() już zwróciła, poniższe instrukcje są nadal wykonywane. To jest jak idealne zachowanie asynchroniczne.

Powyższy program został przepisany poniżej, biorąc pod uwagę przyszłą bibliotekę i jej funkcję sync():

#włączać

#włączać

#włączać

za pomocą przestrzeń nazwgodziny;

przyszły<strunowy>wyjście;

sznurek cb(sznurek)

{

powrótspięć;

}



próżniamainFn(wejście ciąg)

{

wyjście=asynchroniczny(cb, wejście);

koszt<<„główna funkcja”<<' ';

}


próżniafn()

{

koszt<<'widziany'<<' ';

}


intGłówny()

{

wejście ciąg=strunowy(„funkcja oddzwaniania”);

mainFn(Wejście);

fn();

sznurek ret=wyjście.dostwać(); //czeka na powrót wywołania zwrotnego, jeśli to konieczne

koszt<<Prawidłowy<<' ';



powrót 0;

}

Funkcja sync() ostatecznie przechowuje dane wyjściowe funkcji zwrotnej w przyszłym obiekcie. Oczekiwany wynik można uzyskać w funkcji main(), używając funkcji członkowskiej get() przyszłego obiektu.

Wniosek

Funkcja wywołania zwrotnego to funkcja będąca argumentem, a nie parametrem w innej funkcji. Schemat funkcji wywołania zwrotnego wymaga funkcji głównej i samej funkcji wywołania zwrotnego. Deklaracja funkcji zwrotnej jest częścią listy parametrów funkcji głównej. Definicja funkcji zwrotnej jest wskazana w wywołaniu funkcji głównej funkcji (w main()). Funkcja wywołania zwrotnego jest faktycznie wywoływana w definicji funkcji głównej.

Schemat funkcji wywołania zwrotnego niekoniecznie jest asynchroniczny. Aby mieć pewność, że schemat funkcji wywołania zwrotnego jest asynchroniczny, wprowadź główne dane wejściowe kodu, a dane wejściowe funkcji wywołania zwrotnego; zrobić główne wyjście kodu, wyjście funkcji zwrotnej; przechowuj dane wyjściowe funkcji zwrotnej w zmiennej lub strukturze danych. W funkcji main(), po wywołaniu funkcji głównej, wykonaj inne instrukcje aplikacji. Gdy potrzebne jest wyjście funkcji zwrotnej, w funkcji main() użyj jej (odczytaj i wyświetl) od czasu do czasu.