Использование с указателями на функции члены 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Использование с указателями на функции члены



Помимо указателей на глобальные функции и функторов, в качестве первого параметра можно указывать указатель на функцию-член какого-то класса. Предположим один из простейших вариантов использования:

class GraphicsObject

{

public:

void Draw(Canvas* canvas, DrawModes mode);

};

// Объявляем коллекцию объектов этого класса:

std::list<GraphicsObject*> GraphObjects;

// А теперь нам нужно отрисовать все объекты коллекции на заданном канвасе

std::for_each(GraphObjects.begin(), GraphObjects.end(); ……..);

при использовании чистого STL нам бы пришлось писать свой собственный функтор, вызывающий у переданного объекта метод Draw с заданными параметрами. Но можно воспользоваться boost::bind:

std::for_each(GraphObjects.begin(), GraphObjects.end(),

boost::bind(&GraphObject::Draw, _1, canvas, mode));

в результате чего мы получаем требуемый результат. У всех объектов коллекции вызывается метод Draw, которому передаются параметры canvas и mode. При использовании указателя на функцию-член класса необходимо помнить о том, что первым параметром ей должен передаваться указатель на объект, для которого эта функция должна вызываться. В приведенном примере мы указываем, что метод должен вызываться у переданного в bind единственного параметра. Но никто не мешает нам в качестве первого передаваемого параметра указать this, или любой другой указатель. Но, как говориться в современных набивших оскомину рекламах, это еще не все.

Использование с указателем на член данных

Помимо указателей на члены-функции можно использовать указатели на члены данных. Синтаксис подобный и правила использования при этом почти такие же. Возьмем задачу. Есть структура и вектор ее экземпляров:

struct Point

{

 

int x;

int y;

};

 

std::vector<Point> PointsArray;

Теперь надо удалить из этого массива все элементы, у которых координата x равна нулю. Сделать это просто:

std::remove_if(PointsArray.begin(), PointsArray.end(),

boost::bind(std::equal_to<int>(), boost::bind(&Point::x, _1), 0));

Т. е. для каждого элемента массива вызывается вложенный связыватель, который возвращает значение соответствующего элемента структуры, после чего производится его сравнение с нулем. Тут мы видим еще одну возможность связывателей:

Каскадное использование связывателей

Связыватели могут вкладываться друг в друга. Один из вариантов такого вкладывания проиллюстрирован предыдущем примером. Единственное, что в этом случае надо «держать в голове» - это то, что плейсхолдеры, вне зависимости от того, в каком bind'ере (по уровню вложенности) они находятся, «адресуют» параметры самого внешго binder'а. Усложним предыдущий пример. Нужно выбросить все точки, имеющие нулевые координаты:

std::remove_if(PointsArray.begin(), PointsArray.end(), boost::bind(

 

std::logical_and(),

boost::bind(std::equal_to<int>(), boost::bind(&Point::x, _1), 0),

boost::bind(std::equal_to<int>(), boost::bind(&Point::y, _1), 0)

 

)

));

Это хотя и работает так, как хочется, но выглядит слишком наворочено. По этому начиная с версии 1.33 в boost::bind появилась новая возможность -

Перегруженные операторы

Для упрощения приведенных выше многоэтажных конструкций для boost::bind (начиная с версии 1.33 boost'а) перегружены следующие операторы:!, ==, !=, <,?, > и >=. Таким образом, приведенное выше выражение упрощается до:

std::remove_if(PointsArray.begin(), PointsArray.end(), boost::bind(

 

boost::bind(&Point::x, _1) == 0 && boost::bind(&Point::y, _1) == 0)

 

));

Использование ссылок

Одна из неприятностей заключается в том, что если связываемая функция принимает какой-то из своих аргументов по ссылке, то с использованием boost::bind его (напрямую) передать нельзя. Т. е. например, в следующем случае:

void foo(int& a, int& b);

 

int a = 1, b = 2;

boost::bind(foo, a, b);

аргументы a и b по ссылке переданы не будут. Для того, чтобы действительно передать ссылки, необходимо делать такой вызов:

boost::bind(foo, boost::ref(a), boost::ref(b));

В этом случае foo, вызываемый из связывателя, будет действительно работать со ссылками на соответствующие переменные.

Полную документацию и примеры использования можно найти в оригинальной документации: http://www.boost.org/libs/bind/bind.html

Пример использования.

#include <vector>

#include <boost/bind.hpp>

#include <boost/function.hpp>

class Test

{

public:

void f_1()

{

std::cout << "void f()" << std::endl;

}

void f_2(int i)

{

std::cout << "void f_2(): " << i << std::endl;

}

void f_3(const int &i)

{

std::cout << "void f_2(): " << i << std::endl;

}

void two_params(int a, int b)

{

std::cout << "void two_params(int a, int b): " << a << ", " << b << std::endl;

}

};

 

class Test2

{

public:

void do_stuff(const std::vector<int>& v)

{

std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));

}

};

 

void test(const std::string &a)

{

std::cout << "void test(const std::string &a). a = " << a << std::endl;

}

 

void prn(boost::function<void(int)> fn, int a)

{

fn(a);

}

 

int _tmain(int argc, _TCHAR* argv[])

{

////////////////////// bind //////////////////////

std::cout << "boost::bind test" << std::endl;

Test a;

boost::bind(&Test::f_1, &a)();

 

boost::bind(&Test::f_2, &a, 1)();

int test_int(2);

boost::bind(&Test::f_2, &a, _1)(test_int);

 

boost::bind(&Test::f_3, &a, 3)();

int test_int_2(4);

boost::bind(&Test::f_3, &a, _1)(test_int_2);

 

int one(100), two(200);

boost::bind(&Test::two_params, &a, _1, _2)(one, two);

boost::bind(&Test::two_params, &a, _2, _1)(one, two);

 

std::string test_str("Hi there.");

boost::bind(&test, test_str)();

boost::bind(&test, _1)(test_str);

std::cout << std::endl;

 

////////////////////// function //////////////////////

std::cout << "boost::function test" << std::endl;

 

boost::function<void(void)> func;

func = boost::bind(&Test::f_1, &a);

func();

func = boost::bind(&Test::f_2, &a, 201);

func();

func = boost::bind(&Test::f_3, &a, 202);

func();

func = boost::bind(&Test::two_params, &a, 203, 204);

func();

 

boost::function<void(int)> func_2;

func_2 = boost::bind(&Test::f_2, &a, _1);

prn(func_2, 301);

func_2 = boost::bind(&Test::f_3, &a, _1);

prn(func_2, 302);

 

int i_303(303), i_304(304), i_305(305), i_306(306);

func_2 = boost::bind(&Test::two_params, &a, i_303, _1);

prn(func_2, 1);

func_2 = boost::bind(&Test::two_params, &a, _1, i_304);

prn(func_2, 1);

 

Test2 t;

std::vector<int> vec;

vec.resize(20);

std::generate_n(vec.begin(), 20, rand);

std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));

//simple_bind(&Test::do_stuff, t, _1)(vec);

//boost::bind(&Test2::do_stuff, t, _1)(vec);

 

return 0;

}

 


23.5. BOOST::ASIO (::IO_SERVICE) (Самостоятельное изучение)

Boost::asio основы

boost::asio на самом деле, хоть и называется библиотекой асинхронной, но может выполнять и синхронные, и асинхронные операции. Вся библиотека при этом завязана на объекты класса boost::asio::io_service – именно asio::io_service предоставляет программе связь с нативными объектами ввода/вывода. Соответственно, что бы ваша программа могла работать с boost::asio, нужно создать хотя бы один объект этого класса и уже после этого “аттачить” все другие объекты к нему, например вот так:

boost::asio::io_service io_service;boost::asio::ip::tcp::socket socket(io_service);

Подключить только что созданный сокет к серверу тоже не представляет из себя проблемы:

boost::system::error_code ec; // здесь будет записана ошибка, если она былаsocket.connect(server_endpoint, ec); // server_endpoint задаёт куда подключаемся

После того как мы подключили объекты ввода-вывода (в данном случае socket) к asio::io_service – этот сервис будет осуществлять взаимодействие с операционной системой и получать/отправлять данные.

Boost::asio асинхронный

Тот вариант, что мы рассмотрели выше – это синхронное взаимодействие, т.е., в данном случае это значит, что пока происходит подключение – наша программа “зависнет” до того момента, пока подключение не выполнится, либо не произойдёт ошибка. Если же мы хотим что бы работа boost::asio была асинхронной, надо идти немного другим путём, который тоже предусмотрен в библиотеке. Для работы с асинхронными операциями мы должны передать в функцию так называемый completion handler, или, говоря по русски, функцию, которая будет вызвана, когда операция завершится (т.е. либо подключится, либо возникнет ошибка):

void your_completion_handler(const boost::system::error_code& ec){// тут обрабатываем подключение либо ошибку} boost::asio::io_service io_service;boost::asio::ip::tcp::socket socket(io_service);socket.async_connect(server_endpoint, your_completion_handler);

Все асинхронные функции в boost::asio работают именно по такому принципу. С одной стороны, это довольно удобно для программирования – логика работы каждой из функций отделена от других, с другой стороны это может может показаться Вам немного непривычным. Но, я уверен, Вы освоитесь

Использование boost::asio

Приведу короткий и простой пример использования boost::asio, это код примера из самого boost, лишь перевёл комментарии:

#include <iostream>

#include <istream>

#include <ostream>

#include <string>

#include <boost/asio.hpp>

 

using boost::asio::ip::tcp;

 

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

{

try

{

if (argc!= 3) // если аргументы не заданы или заданы не все

{

std::cout << "Usage: sync_client <server> <path>\n";

std::cout << "Example:\n";

std::cout << " sync_client www.boost.org /LICENSE_1_0.txt\n";

return 1;

}

 

boost::asio::io_service io_service; // основной класс asio

 

// Получаем список конечных точек для указанного сервера

tcp::resolver resolver(io_service);

tcp::resolver::query query(argv[1], "http");

tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

tcp::resolver::iterator end;

 

// Перебираем эти конечные точки и пробуем подключиться

tcp::socket socket(io_service);

boost::system::error_code error = boost::asio::error::host_not_found;

while (error && endpoint_iterator!= end)

{

socket.close();

socket.connect(*endpoint_iterator++, error);

}

if (error) // подключиться не удалось

throw boost::system::system_error(error);

 

// Формируем запрос к веб-серверу. Указываем "Connection: close" что бы

// сервер закрыл соединение как только отправит нам данные. Это

// позволит нам узнать что отправка завершенна как только возникнет EOF

boost::asio::streambuf request;

std::ostream request_stream(&request);

request_stream << "GET " << argv[2] << " HTTP/1.0\r\n";

request_stream << "Host: " << argv[1] << "\r\n";

request_stream << "Accept: */*\r\n";

request_stream << "Connection: close\r\n\r\n";

 

// Отправляем сформированный запрос веб-серверу.

boost::asio::write(socket, request);

 

// Читаем ответ сервер. streambuf response примет все данные

// он сам будет увеличиваться по мере поступления данных от сервера.

boost::asio::streambuf response;

boost::asio::read_until(socket, response, "\r\n");

 

// Проверяем что бы не было ошибок.

std::istream response_stream(&response);

std::string http_version;

response_stream >> http_version;

unsigned int status_code;

response_stream >> status_code;

std::string status_message;

std::getline(response_stream, status_message);

if (!response_stream || http_version.substr(0, 5)!= "HTTP/") // ошибка

{

std::cout << "Invalid response\n";

return 1;

}

if (status_code!= 200) // если код ответа не 200, то это тоже ошибка

{

std::cout << "Response returned with status code " << status_code << "\n";

return 1;

}

 

// Читаем ответ. Он закончится пустой строкой.

boost::asio::read_until(socket, response, "\r\n\r\n");

 

// Парсим заголовки

std::string header;

while (std::getline(response_stream, header) && header!= "\r")

std::cout << header << "\n";

std::cout << "\n";

 

// Выводим в лог

if (response.size() > 0)

std::cout << &response;

 

// Теперь читаем до конца

while (boost::asio::read(socket, response,

boost::asio::transfer_at_least(1), error))

std::cout << &response;

 

if (error!= boost::asio::error::eof) // ошибка

throw boost::system::system_error(error);

}

catch (std::exception& e) // возникло исключение

{

std::cout << "Exception: " << e.what() << "\n";

}

 

return 0;

}

 

 

Вот эти 100 строк кода работают как HTTP-клиент и позволяют нам читать страницы с веб-серверов. Мне кажется, что код не сложный, особенно, когда он с комментариями.



Поделиться:


Последнее изменение этой страницы: 2016-12-11; просмотров: 205; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.206.76.160 (0.084 с.)