У меня есть класс, который должен иметь возможность отправлять сообщения по TCP. Вот упрощенный интерфейс:
class CommandScreenshot : public CameraCommand
{
public:
CommandScreenshot();
~CommandScreenshot();
void Dispatch(boost::shared_ptr<boost::asio::io_service> io_service);
private:
void resolve_handler(const boost::system::error_code& err,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
};
Как видите, у меня есть функция Dispatch
, которая на самом деле предназначена только для запуска асинхронной операции:
void CommandScreenshot::Dispatch(boost::shared_ptr<boost::asio::io_service> io_service)
{
boost::asio::ip::tcp::resolver resolver(*io_service);
boost::asio::ip::tcp::resolver::query query(m_hostname,"http");
resolver.async_resolve(query,boost::bind(&CommandScreenshot::resolve_handler,this,boost::asio::placeholders::error, boost::asio::placeholders::iterator));
return;
}
Все остальное будет сделано в следующих функциях обратного вызова. Объект io_service
, а также соответствующий поток управляются другим классом (который имеет экземпляр CommandScreenshot
и вызывает функцию Dispatch
).
Теперь, чтобы реализовать простое TCP-соединение с Boost, вам нужны объекты resolver
и socket
, привязанные к объекту io_service
. Поскольку объект io_service
будет передан только во время вызова функции, я не могу инициализировать их в конструкторе класса. Также невозможно объявить их членами класса, а затем просто инициализировать в самой функции.
Моей первой идеей было просто инициализировать их при вызове функции и передать обработчику завершения. Это означало бы, что я объявляю оба объекта каждый раз, когда вызывается функция, и привязываю их к io_service
. Затем в async_resolve
я добавляю оба параметра через boost::bind
. Это означало бы, что мой resolve_handler
будет ожидать больше аргументов, например:
void resolve_handler(const boost::system::error_code& err,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
boost::asio::ip::tcp::resolver resolver,
boost::asio::ip::tcp::socket socket);
Я действительно сомневаюсь, что это достойное и справедливое решение. Обычно эти объекты следует хранить как элементы, а не копировать. Так что я еще раз подумал, и мой разум привел меня к boost::shared_ptr
.
В моем заголовке это выглядит сейчас так:
// Stuff above stays the same
private:
boost::shared_ptr<boost::asio::ip::tcp::resolver> m_resolver;
boost::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
// Stuff below stays the same
И реализация будет:
void CommandScreenshot::Dispatch(boost::shared_ptr<boost::asio::io_service> io_service)
{
m_resolver.reset(new boost::asio::ip::tcp::resolver(*io_service));
m_socket.reset(new boost::asio::ip::tcp::socket(*io_service));
boost::asio::ip::tcp::resolver::query query(m_hostname,"http");
m_resolver->async_resolve(query,boost::bind(&CommandScreenshot::resolve_handler,this,boost::asio::placeholders::error, boost::asio::placeholders::iterator));
return;
}
При этом мне не нужно копировать около 4 (или, может быть, даже больше) параметров и связывать их вместе. Когда мне нужен объект сокета, я могу просто получить к нему доступ через указатель, который является членом класса.
Теперь мой простой вопрос -> это правильный путь? Функцию можно вызывать несколько раз, даже если асинхронная часть еще не завершена. (Я знаю, что тогда я должен защитить сокет и преобразователь с помощью мьютекса). Но чисто ли это, что я каждый раз создаю новый объект, когда вызываю функцию Dispatch
? Достаточно ли вызова reset
, чтобы избавиться от ненужной памяти?
Я знаю, что это длинный текст для конкретного короткого вопроса, и, кроме того, в нем даже нет ошибки. Но мне всегда хотелось бы знать, достойный ли это путь, которым я иду, и есть ли лучший путь, как бы я поступил.