Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Лямбда-функции и реализация комфортного механизма Callback-ов на C

Anna | 24.06.2014 | нет комментариев
В этой статье на примере реализации механизма обратного вызова будет рассмотрена вероятности использования лямбда функций в комфортной и стремительной форме.

Постановка задачи

Нужно реализовать комфортный и стремительный механизм сохранения «указателя» на произвольную функцию и дальнейшего его вызова с передачей довода (для примера возьмём тип char*).

Способ 1 – на классическом «Си»

Решая задачу «в лоб» дозволено получить что-то как бы такого:

//Определение нашей функции
static void MyFunction(char *s){
	puts(s);
}

int main(){
	//Переменная, хранящая указатель на функцию
	void (*MyCallback)(char *argument);

	//Сохранение указателя на нашу функцию
	MyCallback=MyFunction;

	//Вызов функции по указателю
	MyCallback("123");

	return 0;
}

Механизм дюже примитивный и Зачастую применяемый. Но при большом числе обратных вызовов их объявление становиться не дюже комфортным.

Лямбда-функции в С

Для тех кто не слышал про С 11 (либо С 0x) либо пока ещё не коснулся его, расскажу про некоторые нововведения из этого эталона. В С 11 возникло ключевое слово auto, которое может ставиться взамен типа при объявлении переменной с инициализацией. При этом тип переменной будет одинаков типу, казанному позже «=». Скажем:

	auto a=1;     // тоже самое что  int         a=1;
	auto b="";    // тоже самое что  const char* b=1;
	auto c=1.2;   // тоже самое что  double      c=1;
	auto d;	// оплошность! немыслимо определить тип переменной d

Но самое увлекательное это лямбда-функции. В тезисе, это обыкновенные функции, но которые могут быть объявлены прямо в выражении:

[](int a,int b) -> bool //лямбда функция с двумя доводами, возвращает bool
{
	return a>b;
}

Синтаксис лямбда функции таков:

[захватываемые переменные](доводы)->возвращаемый тип{ тело функции }

Кусок «->возвращаемый тип» может отсутствовать. Тогда подразумевается «->void». Ещё пример применения:

int main(int argc,char *argv[]){

	//функция, аналогичная abs(int)
	auto f1=[](int a)->int{
		return (a>0)?(a):(-a);
	};

	//функция, возвращающая случайное значение от 0.0 до 1.0
	auto f2=[]()->float{
		return float(rand())/RAND_MAX;
	};

	//функция, ждущая нажатия enter
	auto f3=[](){
		puts("Press enter to continue...");
		getchar();
	};

	printf("%d %dn",f1(5),f1(-10));
	printf("%f %fn",f2(),f2());
	f3();

	return 0;
}

Данная программа выведет:

5 10
0.563585 0.001251
Press enter to continue...

В этом примере были объявлены и проинициализированы три переменные (f1,f2 и f3) типа auto, следственно тип которых соответствует типу стоящему справа – типу лямбда функций.
Лямбда функция, сама по себе, не является указателем на функцию (правда в ряде случаев может бытьприведена к нему). Компилятор вызывает функцию не по адресу а по её типу – именно следственно у всякой лямбда функции свой тип, скажем «<lambda_a48784a181f11f18d942adab3de2ffca>». Такой тип немыслимо указать, следственно его дозволено применять только в связке с auto либо образцами (там тип тоже может механически быть определён).
Эталон так же допускает реформирование от типа лямбда к типу указателя на функцию, в случае отсутствия захватываемых переменных:

void(*func)(int arg);
func= [](int arg){ ... }; // была лямбда, стала указатель

Захватываемые переменные это те переменные, которые «попадают вовнутрь» лямбда функции при её указании:

int main(int argc,char *argv[]){
	auto f=[argc,&argv](char *s){
		puts(s);
		for(int c=0;c<argc;c  ){
			puts(argv[c]);
		}
	};

	f("123");

	return 0;
}

Эти параметры, реально и сохраняются (копируются по значению) в переменной f.
Если указать знак & перед именем, то парам

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB