Чтение онлайн

ЖАНРЫ

C++. Сборник рецептов

Когсуэлл Джефф

Шрифт:

12.5. Передача аргумента функции потока

Проблема

Требуется передать аргумент в вашу функцию потока, однако средствами библиотеки Boost Threads предусматривается передача только функторов без аргументов.

Решение

Создайте адаптер функтора, который принимает ваши параметры и возвращает функтор без параметров. Адаптер функтора можно использовать там, где должен был бы быть функтор потока. Пример 12.6 показывает, как это можно сделать.

Пример 12.6. Передача аргументов функции потока

#include <iostream>

#include <string>

#include <functional>

#include <boost/thread/thread.hpp>

// typedef
используется для того, чтобы приводимые ниже объявления лучше

// читались

typedef void (*WorkerFunPtr)(const std::string&);

template<typename FunT, // Тип вызываемой функции

 typename ParamT> // тип ее параметра

struct Adapter {

 Adapter(FunT f, ParamT& p) : // Сконструировать данный адаптер и

f_(f), p_(&p) {} // установить члены на значение функции и ее

// аргумента

 void operator { // Просто вызов функции с ее аргументом

f_(*p_);

 }

private:

 FunT f_;

 ParamT* p_; // Использовать адрес параметра. чтобы избежать лишнего

// копирования

};

void worker(const std::string& s) {

 std::cout << s << '\n';

}

int main {

 std::string s1 = "This is the first thread!";

 std::string s2 = "This is the second thread!";

 boost::thread thr1(Adapter<WorkerFunPtr, std::string>(worker, s1));

 boost::thread thr2(Adapter<WorkerFunPtr, std::string>(worker, s2));

 thr1.join;

 thr2.join;

}

Обсуждение

Здесь приходится решать принципиальную проблему, причем характерную не только для потоков или проекта Boost, а общую проблему, возникающую при необходимости передачи функтора с одной сигнатурой туда, где требуется другая сигнатура. Решение состоит в создании адаптера.

Синтаксис может показаться немного путаным, но фактически в примере 12.6 создается временный функтор, который может вызываться конструктором потока как функция без аргументов (требуется именно такая функция). Но прежде всего используйте

typedef
, чтобы указатель функции лучше воспринимался в тексте.

typedef void (*WorkerFunPtr)(const std::string&);

Это создает тип

WorkerFunPtr
, который является указателем на функцию, принимающую по ссылке аргумент типа
string
и возвращающую тип
void
. После этого я создал шаблон класса
Adapter
. Он обеспечивает инстанцирование динамического функтора. Обратите внимание на конструктор:

template<Typename FunT,

 typename ParamT>

struct Adapter {

 Adapter(FunT f, ParamT& p) : f_(f), p_(&p) {}

 // ...

Конструктор

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

Теперь рассмотрим следующую строку главного потока.

boost::thread thr1(Adapter<WorkerFunPtr, std::string>(worker, s1))

Аргумент конструктора

thr1
представляет собой реализацию шаблона класса
Adapter
, использующую в качестве параметров два типа
WorkerFunPtr
и
std::string
. Это именно те два типа, которые являются членами адаптера
f_
и
p_
. Наконец,
Adapter
перегружает
operator
, поэтому он может вызываться как функция. Его вызов означает просто выполнение следующей функции.

f_(*p_);

Применяя шаблон класса

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

Глава 13

Интернационализация

13.0. Введение

В данной главе приводятся решения некоторых задач, которые обычно возникают при интернационализации программ С++. Обеспечение возможности работы программы в различных регионах (это обычно называется локализацией), как правило, требует решения двух задач: форматирования строк, воспринимаемых пользователем, в соответствии с местными соглашениями (например, даты, времени, денежных сумм и чисел) и обеспечения работы с различными символьными наборами. В настоящей главе рассматривается в основном первая проблема и только кратко вторая, потому что имеется немного возможностей по стандартизации поддержки различных символьных наборов из-за того, что такая поддержка во многом зависит от реализации.

Большая часть программного обеспечения будет работать в странах, отличных от той, где они были написаны. Для поддержки этой практики стандартная библиотека C++ имеет несколько средств, способствующих написанию программного кода, предназначенного для работы в различных странах. Однако они спроектированы не так. как многие другие средства стандартной библиотеки, например строки, файловый ввод-вывод, контейнеры, алгоритмы и т.п. Например, класс, представляющий локализацию, имеет имя

locale
и содержится в заголовочном файле
<lосаlе>
. Класс
locale
предоставляет средства для записи и чтения потоков с применением специфичного для данной местности форматирования и получения таких сведений о локализации, как, например, ее символ валюты или формат даты. Однако стандартом предусматривается обеспечение только одной локализации, и этой локализацией является «C»-локализация, или классическая локализация. Классическая локализация использует стандарт ANSI С: принятые в американском варианте английского языка соглашения по форматированию и 7-битовой код ASCII. И от реализации зависит, будут ли обеспечены экземпляры locale для других языков и регионов.

Поделиться с друзьями: