Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Advanced Topics

Boost , Chapter 1. Boost.LocalFunction 1.0.0 , Chapter 1. Boost.LocalFunction 1.0.0

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Этот раздел иллюстрирует расширенное использование этой библиотеки. Внизу также есть список известных ограничений этой библиотеки.

Эта библиотека позволяет задать значения по умолчанию для локальных параметров функции. Однако обычный синтаксис C++ для параметров по умолчанию, использующий символ присваивания<=>, не может быть использован.Вместо этого используется ключевое слово<default>:

parameter-type parameter-name, default parameter-default-value, ...

Например, давайте запрограммируем локальную функцию<add(x, y)>, где второй параметр<y>является необязательным и имеет значение по умолчанию<2>(см. также).<add_default.cpp>:

int BOOST_LOCAL_FUNCTION(int x, int y, default 2) { // Default parameter.
    return x + y;
} BOOST_LOCAL_FUNCTION_NAME(add)
BOOST_TEST(add(1) == 3);

Программисты могут определить макрос<WITH_DEFAULT>, похожий на следующий, если они думают, что он улучшает читаемость по вышеупомянутому синтаксису (см. также<add_with_default.cpp>):

#define WITH_DEFAULT , default

int BOOST_LOCAL_FUNCTION(int x, int y WITH_DEFAULT 2) { // Default.
    return x + y;
} BOOST_LOCAL_FUNCTION_NAME(add)
BOOST_TEST(add(1) == 3);

Препроцессор C++ не допускает запятые<,>в макропараметрах, если они не обернуты круглым скобком<()>(см.Boost.Utility/Identity). Типдокументации для деталей. Таким образом, использование запятых в локальных функциональных параметрах и связываниях будет генерировать (зашифрованные) ошибки препроцессора, если они не обернуты дополнительным набором круглых скобок<()>, как описано здесь.

[Note]Note

Также макро-параметры с запятыми, обернутыми угловым скобком<<>>(шаблоны и т.д.) или квадратным скобком<[]>(доступ к многомерной матрице и т.д.), должны быть обернуты дополнительным круглым скобком<()>, как объясняется здесь (это потому, что препроцессор распознает только круглый скобок и не распознает угловой, квадратный или любой другой тип скобок). Однако макропараметры с запятыми, которые уже обернуты круглыми скобками<()>, хороши (вызовы функций, некоторые выражения значений и т. д.).

Кроме того, локальные типы параметров функции не могут начинаться с неалфавитных символов (алфавитные символы<A-Z>,<a-z>и<0-9>).Библиотека будет генерировать (зашифрованные) ошибки препроцессора, если тип параметра начинается с буквенно-цифрового символа.

Рассмотрим следующий пример:

void BOOST_LOCAL_FUNCTION(
    const std::map<std::string, size_t>& m,                 // (1) Error.
    ::sign_t sign,                                          // (2) Error.
    const size_t& factor,
            default key_sizeof<std::string, size_t>::value, // (3) Error.
    const std::string& separator, default cat(":", " ")     // (4) OK.
) {
    ...
} BOOST_LOCAL_FUNCTION_NAME(f)

(1)Тип параметра<const std::map<std::string,size_t>&>содержит запятую<,>после первого параметра шаблона<std::string>. Эта запятая не обернута никаким круглым скобком<()>, таким образом, это вызовет ошибку препроцессора.<,>Для преодоления этой проблемы можно использовать макрос<BOOST_IDENTITY_TYPE((><type-with-commas><))>, определенный в заголовке<boost/utility/identity_type.hpp>, чтобы обернуть тип в дополнительную скобку<()>:

#include <boost/utility/identity_type.hpp>
void BOOST_LOCAL_FUNCTION(
    BOOST_IDENTITY_TYPE((const std::map<std::string, size_t>&)) m, // OK.
    ...
) {
    ...
} BOOST_LOCAL_FUNCTION_NAME(f)

Этот макрос расширяется до выражения, которое оценивает (во время компиляции) точно по заданному типу (более того, этот макрос не использует вариадные макросы, поэтому он работает на любом компилятореC++03). Обратите внимание, что в общей сложности необходимо два набора скобок<()>: Скобки для вызова макроса<BOOST_IDENTITY_TYPE(...)>плюс скобки для обертывания выражения типа (и, следовательно, любой запятой<,>, которую он содержит) переданы в качестве параметра макро<BOOST_IDENTITY_TYPE((...))>. Наконец, макрос<BOOST_IDENTITY_TYPE>должен быть префиксирован ключевым словом<typename><typenameBOOST_IDENTITY_TYPE(><parenthesized-type><)>при использовании вместе с макросом<BOOST_LOCAL_FUNCTION_TPL>в шаблонах.

[Note]Note

Часто есть лучшие способы преодолеть это ограничение, которые приводят к более читаемому коду, чем тот, который использует макрос<BOOST_IDENTITY_TYPE>.

Например, в этом случае для получения следующего действительного и, возможно, более читаемого кода можно было бы использовать<typedef>из области охвата:

typedef std::map<std::string, size_t> map_type;
void BOOST_LOCAL_FUNCTION(
    const map_type& m, // OK (and more readable).
    ...
) BOOST_LOCAL_FUNCTION_NAME(f)

(2)Тип параметра<::sign_t>начинается с буквенно-цифровых символов<::>, таким образом, он будет генерировать ошибки препроцессора, если используется в качестве типа параметра локальной функции. Для решения этой проблемы можно использовать макрос<BOOST_IDENTITY_TYPE>:

void BOOST_LOCAL_FUNCTION(
    ...
    BOOST_IDENTITY_TYPE((::sign_t)) sign, // OK.
    ...
) {
    ...
} BOOST_LOCAL_FUNCTION_NAME(f)
[Note]Note

Часто есть лучшие способы преодолеть это ограничение, которые приводят к более читаемому коду, чем тот, который использует макрос<BOOST_IDENTITY_TYPE>.

Например, в этом случае символы<::>можно было просто отбросить, чтобы получить следующий действительный и, возможно, более читаемый код:

void BOOST_LOCAL_FUNCTION(
    ...
    sign_t sign, // OK (and more readable).
    ...
) {
    ...
} BOOST_LOCAL_FUNCTION_NAME(f)

(3)Значение параметра по умолчанию<key_sizeof<std::string,size_t>::value>содержит запятую<,>после первого параметра шаблона<std::string>. Опять же, эта запятая не обернута никаким скобком<()>, поэтому она вызовет ошибку препроцессора. Поскольку это выражение значения (а не выражение типа), оно может быть просто завернуто в дополнительный набор круглых скобок<()>:

void BOOST_LOCAL_FUNCTION(
    ...
    const size_t& factor,
            default (key_sizeof<std::string, size_t>::value), // OK.
    ...
) {
    ...
} BOOST_LOCAL_FUNCTION_NAME(f)

(4)Значение параметра по умолчанию<cat(":"," ")>вместо этого прекрасно, потому что оно содержит запятую<,>, которая уже обернута скобкой<()>вызова функции<cat(...)>.

Рассмотрим следующий полный пример (см. также<macro_commas.cpp>):

void BOOST_LOCAL_FUNCTION(
    BOOST_IDENTITY_TYPE((const std::map<std::string, size_t>&)) m,
    BOOST_IDENTITY_TYPE((::sign_t)) sign,
    const size_t& factor,
            default (key_sizeof<std::string, size_t>::value),
    const std::string& separator, default cat(":", " ")
) {
    // Do something...
} BOOST_LOCAL_FUNCTION_NAME(f)

Локальные функции являются функциональными объектами, поэтому их можно назначать другим функторам, таким какBoost.Function's<boost::function>, чтобы хранить локальную функцию в переменной, передавать ее в качестве параметра другой функции или возвращать ее из ограждающей функции.

Например (см. также<return_assign.cpp>):

void call1(boost::function<int (int) > f) { BOOST_TEST(f(1) == 5); }
void call0(boost::function<int (void)> f) { BOOST_TEST(f() == 5); }
boost::function<int (int, int)> linear(const int& slope) {
    int BOOST_LOCAL_FUNCTION(const bind& slope,
            int x, default 1, int y, default 2) {
        return x + slope * y;
    } BOOST_LOCAL_FUNCTION_NAME(lin)
    boost::function<int (int, int)> f = lin; // Assign to local variable.
    BOOST_TEST(f(1, 2) == 5);
    call1(lin); // Pass to other functions.
    call0(lin);
    return lin; // Return.
}
void call(void) {
    boost::function<int (int, int)> f = linear(2);
    BOOST_TEST(f(1, 2) == 5);
}

[Warning]Warning

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

Кроме того, локальная функция может связывать и вызывать другие локальные функции. Локальные функции всегда должны быть связаны постоянной ссылкой<const bind&>, чтобы избежать ненужных копий. Например, следующая локальная функция<inc_sum>связывает локальную функцию<inc>, поэтому<inc_sum>может вызывать<inc>(см. aslo<transform.cpp>):

int offset = 5;
std::vector<int> v;
std::vector<int> w;
for(int i = 1; i <= 2; ++i) v.push_back(i * 10);
BOOST_TEST(v[0] == 10); BOOST_TEST(v[1] == 20);
w.resize(v.size());
int BOOST_LOCAL_FUNCTION(const bind& offset, int i) {
    return ++i + offset;
} BOOST_LOCAL_FUNCTION_NAME(inc)
std::transform(v.begin(), v.end(), w.begin(), inc);
BOOST_TEST(w[0] == 16); BOOST_TEST(w[1] == 26);
int BOOST_LOCAL_FUNCTION(bind& inc, int i, int j) {
    return inc(i + j); // Call the other bound local function.
} BOOST_LOCAL_FUNCTION_NAME(inc_sum)
offset = 0;
std::transform(v.begin(), v.end(), w.begin(), v.begin(), inc_sum);
BOOST_TEST(v[0] == 27); BOOST_TEST(v[1] == 47);

Местные функции можно складывать друг в друга. Например (см. также<nesting.cpp>):

int x = 0;
void BOOST_LOCAL_FUNCTION(bind& x) {
    void BOOST_LOCAL_FUNCTION(bind& x) { // Nested.
        x++;
    } BOOST_LOCAL_FUNCTION_NAME(g)
    x--;
    g(); // Nested local function call.
} BOOST_LOCAL_FUNCTION_NAME(f)
f();

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

BOOST_LOCAL_FUNCTION_TYPEOF(bound-variable-name)

Макро<BOOST_LOCAL_FUNCTION_TYPEOF>расширяется до выражения типа, которое оценивает (на время компиляции) полностью квалифицированный тип связанной переменной с заданным именем. Это выражение типа полностью квалифицировано в том смысле, что оно будет постоянным, если переменная связана константой<const bind[&]>, и оно также будет ссылкой, если переменная связана ссылкой<[const] bind&>(при необходимости программисты могут удалить<const>и<&>квалификаторы, используя<boost::remove_const>и<boost::remove_reference>, см.Boost.TypeTraits).

Выведенный связанный тип может использоваться в организме для проверки понятий, объявления локальных переменных и т.д. Например (см. также<typeof.cpp>и<addable.hpp>):

int sum = 0, factor = 10;
void BOOST_LOCAL_FUNCTION(const bind factor, bind& sum, int num) {
    // Type-of for concept checking.
    BOOST_CONCEPT_ASSERT((Addable<boost::remove_reference<
            BOOST_LOCAL_FUNCTION_TYPEOF(sum)>::type>));
    // Type-of for declarations.
    boost::remove_reference<BOOST_LOCAL_FUNCTION_TYPEOF(
            factor)>::type mult = factor * num;
    sum += mult;
} BOOST_LOCAL_FUNCTION_NAME(add)
add(6);

В шаблонах<BOOST_LOCAL_FUNCTION_TYPEOF>не должно быть приставлено к ключевому слову<typename>, но в случае возможных манипуляций с типом требуется префикс<typename>, как обычно (см. также<typeof_template.cpp>и<addable.hpp>):

template<typename T>
T calculate(const T& factor) {
    T sum = 0;
    void BOOST_LOCAL_FUNCTION_TPL(const bind factor, bind& sum, T num) {
        // Local function `TYPEOF` does not need `typename`.
        BOOST_CONCEPT_ASSERT((Addable<typename boost::remove_reference<
                BOOST_LOCAL_FUNCTION_TYPEOF(sum)>::type>));
        sum += factor * num;
    } BOOST_LOCAL_FUNCTION_NAME_TPL(add)
    add(6);
    return sum;
}

В этом контексте лучше всего использовать макрос<BOOST_LOCAL_FUNCTION_TYPEOF>вместо использованияBoost.Typeof, чтобы уменьшить количество раз, когдаBoost.Typeofвызывается (либо библиотека уже используется внутриBoost). Типодин раз, и в этом случае использование этого макроса не будет использоватьBoost.Typeofснова, или связанный тип переменной явно указан программистами, как показано ниже, и в этом случае использование этого макроса не будет использоватьBoost.Typeofвообще.

Кроме того, в локальном функциональном корпусе можно получить доступ к типу результата с использованием<result_type>, типу первого параметра с использованием<arg1_type>, типу второго параметра с использованием<arg2_type>и т. Д.<()>

Хотя это не требуется, можно явно указать тип связанных переменных, чтобы библиотека не использовала внутреннеBoost.Typeofдля автоматического вывода типов. Когда указано, связанный переменный тип должен следовать за «ключевым словом»<bind>и должен быть обернут в круглую скобку<()>:

bind(variable-type) variable-name           // Bind by value with explicit type.
bind(variable-type)& variable-name          // Bind by reference with explicit type.
const bind(variable-type) variable-name     // Bind by constant value with explicit type.
const bind(variable-type)& variable-name    // Bind by constant reference with explicit type.
bind(class-type*) this_                     // Bind object `this` with explicit type.
const bind(class-type*) this_               // Bind object `this` by constant with explicit type.

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

Библиотека также используетBoost.Typeofдля определения типа результата локальной функции (поскольку этот тип указан вне макроса<BOOST_LOCAL_FUNCTION>). Таким образом, можно также указать тип результата локальной функции как один из<BOOST_LOCAL_FUNCTION>макро параметров, префиксирующих его<return>, поэтому библиотека не будет использоватьBoost.Typeofдля вывода типа результата:

BOOST_LOCAL_FUNCTION_TYPE(return result-type, ...)

Обратите внимание, что тип результата должен быть указан только один раз либо перед макросом (без префикса<return>), либо как один из макро параметров (с префиксом<return>). Как всегда, тип результата может быть<void>для объявления функции, которая ничего не возвращает (так<return void>допускается, когда тип результата указан как один из макро параметров).

Следующий пример определяет все связанные переменные и типы результатов (см. также<add_typed.cpp>):<()>

struct adder {
    adder(void) : sum_(0) {}
    int sum(const std::vector<int>& nums, const int& factor = 10) {
        // Explicitly specify bound variable and return types (no type-of).
        BOOST_LOCAL_FUNCTION(const bind(const int&) factor,
                bind(adder*) this_, int num, return int) {
            return this_->sum_ += factor * num;
        } BOOST_LOCAL_FUNCTION_NAME(add)
        std::for_each(nums.begin(), nums.end(), add);
        return sum_;
    }
private:
    int sum_;
};

Если нет необходимости, рекомендуется не указывать связанные переменные и типы результатов. Пусть библиотека выводит эти типы, чтобы синтаксис локальной функции был более лаконичным, и локальная декларация функции не должна будет меняться, если изменяется связанный переменный тип (уменьшение обслуживания).

[Note]Note

Когда все связанные переменные и типы результатов явно указаны, реализация библиотеки не будет использоватьBoost.Typeof.

Локальные функции могут быть объявленыinline, чтобы увеличить вероятность того, что компилятор сможет сократить время выполнения вызова локальной функции, введя сгенерированный код сборки. Локальная функция объявляется встроенной, префиксируя ее имя ключевым словом<inline>:

result-type BOOST_LOCAL_FUNCTION(parameters) {
    ... // Body.
} BOOST_LOCAL_FUNCTION_NAME(inline name) // Inlining.

При определении локальной функции обратите внимание на следующее:

  • Накомпиляторах, совместимых с C++03, встроенные локальные функции всегда имеют время выполнения, сравнимое с их эквивалентной реализацией, в которой используются локальные функторы (см.). Альтернативы. Однако встроенные локальные функции имеют важное ограничение, что они не могут быть назначены другим функторам (например,<boost::function>), и они не могут быть переданы в качестве параметров шаблона.
  • На компиляторахC++11<inline>эффект не имеет, потому что эта библиотека автоматически генерирует код, который используетC++11специфические функции для включения вызовов локальной функции, когда это возможно, даже если локальная функция не объявлена встроенной. Кроме того, неC++11локальные функции всегда могут быть пропусками в качестве параметров шаблона, даже если они объявлены встроенными.
[Important]Important

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

Например, в строке объявлена следующая локальная функция (таким образом, для переносимости вместо передачи локальной функции в качестве параметра шаблона алгоритму<std::for_each>, см. также<add_inline.cpp>):

int sum = 0, factor = 10;
void BOOST_LOCAL_FUNCTION(const bind factor, bind& sum, int num) {
    sum += factor * num;
} BOOST_LOCAL_FUNCTION_NAME(inline add) // Inlining.
std::vector<int> v(100);
std::fill(v.begin(), v.end(), 1);
for(size_t i = 0; i < v.size(); ++i) add(v[i]); // Cannot use for_each.

Локальные функции могут быть объявленырекурсивными, поэтому локальная функция может рекурсивно называть себя из своего тела (как обычно с функциями C++). Локальная функция объявляется рекурсивной, префиксируя ее имя «ключевым словом»<recursive>(таким образом,<recursive>не может использоваться в качестве названия локальной функции):

result-type BOOST_LOCAL_FUNCTION(parameters) {
    ... // Body.
} BOOST_LOCAL_FUNCTION_NAME(recursive name) // Recursive.

Например, для рекурсивного вычисления факториалов всех чисел в указанном векторе используется следующая локальная функция (см. также<factorial.cpp>):

struct calculator {
    std::vector<int> results;
    void factorials(const std::vector<int>& nums) {
        int BOOST_LOCAL_FUNCTION(bind this_, int num,
                bool recursion, default false) {
            int result = 0;
            if(num <= 0) result = 1;
            else result = num * factorial(num - 1, true); // Recursive call.
            if(!recursion) this_->results.push_back(result);
            return result;
        } BOOST_LOCAL_FUNCTION_NAME(recursive factorial) // Recursive.
        std::for_each(nums.begin(), nums.end(), factorial);
    }
};

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

... BOOST_LOCAL_FUNCTION_NAME(inline recursive factorial)

Рекурсивные локальные функции никогда не следует называть вне сферы их декларирования.

[Warning]Warning

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

Это не является ограничением в отношении лямбда-функцийC++11, потому что лямбды никогда не могут называть себя рекурсивными (другими словами, нет рекурсивной лямбда-функции, которую можно успешно назвать за пределами области ее декларирования, потому что нет рекурсивной лямбда-функции вообще).

Поскольку локальные функции являются функторами, их можно перегрузить, используя<boost::overloaded_function>функтор. Boost.Functional/OverloadedFunctionиз заголовка<boost/functional/overloaded_function.hpp>(см.) Boost.Functional/OverloadedFunctionдокументация для деталей.

В следующем примере объект<add>перегруженной функции может называться с подписями либо от локальной функции<add_s>, либо от локальной функции<add_d>, либо от локальной функции<add_d>с ее дополнительным параметром по умолчанию, либо от указателя функции<add_i>(см. также<overload.cpp>):

int add_i(int x, int y) { return x + y; }

std::string s = "abc";
std::string BOOST_LOCAL_FUNCTION(
        const bind& s, const std::string& x) {
    return s + x;
} BOOST_LOCAL_FUNCTION_NAME(add_s)
double d = 1.23;
double BOOST_LOCAL_FUNCTION(const bind d, double x, double y, default 0) {
    return d + x + y;
} BOOST_LOCAL_FUNCTION_NAME(add_d)
boost::overloaded_function<
      std::string (const std::string&)
    , double (double)
    , double (double, double) // Overload giving default param.
    , int (int, int)
> add(add_s, add_d, add_d, add_i); // Overloaded function object.
BOOST_TEST(add("xyz") == "abcxyz"); // Call `add_s`.
BOOST_TEST((4.44 - add(3.21)) <= 0.001); // Call `add_d` (no default).
BOOST_TEST((44.44 - add(3.21, 40.0)) <= 0.001); // Call `add_d`.
BOOST_TEST(add(1, 2) == 3); // Call `add_i`.

Можно запрограммировать спецификации исключений для локальных функций, указав их после макроса<BOOST_LOCAL_FUNCTION>и перед блоком кода тела<{ ...}>.

[Important]Important

Обратите внимание, что спецификации исключений применяются только к коду тела, указанному программистами, и они не применяются к остальной части кода, автоматически генерируемой макро расширениями для реализации локальных функций. Например, даже если код тела указан, чтобы бросить без исключения с помощью<throw(){...}>, выполнение библиотечного кода, автоматически генерируемого макросами, все равно может бросить (если нет памяти и т. д.).

Например (см. также<add_except.cpp>):

double sum = 0.0;
int factor = 10;
void BOOST_LOCAL_FUNCTION(const bind factor, bind& sum,
        double num) throw() { // Throw nothing.
    sum += factor * num;
} BOOST_LOCAL_FUNCTION_NAME(add)
add(100);

Локальные функциональные параметры поддерживают классификаторы хранения, как обычно вC++03. Классификатор хранения<auto>указан как:<<>>

auto parameter-type parameter-name

Классификатор<register>хранения определяется как:

register parameter-type parameter-name

Например (см. также<add_classifiers.cpp>):

int BOOST_LOCAL_FUNCTION(auto int x, register int y) { // Classifiers.
    return x + y;
} BOOST_LOCAL_FUNCTION_NAME(add)

В общем, невозможно многократно расширить макросы<BOOST_LOCAL_FUNCTION>,<BOOST_LOCAL_FUNCTION_TPL>на одной линии.<[]>

Поэтому эта библиотека предоставляет дополнительные макросы<BOOST_LOCAL_FUNCTION_ID>и<BOOST_LOCAL_FUNCTION_ID_TPL>, которые могут быть расширены несколько раз на одной строке, пока программисты определяют уникальные идентификаторы в качестве первых параметров макросов. Уникальным идентификатором может быть любой токен (не только числовой), который может быть успешно сцеплен препроцессором (например,<local_function_number_1_at_line_123>).<()>

Макросы<BOOST_LOCAL_FUNCTION_ID>и<BOOST_LOCAL_FUNCTION_ID_TPL>принимают локальные списки параметров функций, используя тот же синтаксис, что и<BOOST_LOCAL_FUNCTION>. Например (см. также<same_line.cpp>):

#define LOCAL_INC_DEC(offset) \
    int BOOST_LOCAL_FUNCTION_ID(BOOST_PP_CAT(inc, __LINE__), /* unique ID */ \
            const bind offset, const int x) { \
        return x + offset; \
    } BOOST_LOCAL_FUNCTION_NAME(inc) \
    \
    int BOOST_LOCAL_FUNCTION_ID(BOOST_PP_CAT(dec, __LINE__), \
            const bind offset, const int x) { \
        return x - offset; \
    } BOOST_LOCAL_FUNCTION_NAME(dec)
#define LOCAL_INC_DEC_TPL(offset) \
    T BOOST_LOCAL_FUNCTION_ID_TPL(BOOST_PP_CAT(inc, __LINE__), \
            const bind offset, const T x) { \
        return x + offset; \
    } BOOST_LOCAL_FUNCTION_NAME_TPL(inc) \
    \
    T BOOST_LOCAL_FUNCTION_ID_TPL(BOOST_PP_CAT(dec, __LINE__), \
            const bind offset, const T x) { \
        return x - offset; \
    } BOOST_LOCAL_FUNCTION_NAME_TPL(dec)
template<typename T>
void f(T& delta) {
    LOCAL_INC_DEC_TPL(delta) // Multiple local functions on same line.
    BOOST_TEST(dec(inc(123)) == 123);
}
int main(void) {
    int delta = 10;
    LOCAL_INC_DEC(delta) // Multiple local functions on same line.
    BOOST_TEST(dec(inc(123)) == 123);
    f(delta);
    return boost::report_errors();
}

Как показано в приведенном выше примере, макросы<BOOST_LOCAL_FUNCTION_ID>и<BOOST_LOCAL_FUNCTION_ID_TPL>особенно полезны, когда их необходимо вызывать несколько раз в пределах определяемого пользователем макроса (потому что препроцессор расширяет все вложенные макросы на одной линии).

В следующей таблице приведены все функции C++ с указанием тех функций, которые не поддерживаются этой библиотекой для локальных функций.

C++ Функциональность

Поддержка локальных функций

Комментарий

<export>

Нет.

Это не поддерживается, потому что локальные функции не могут быть шаблонами (плюс большинство компиляторов C++ вообще не реализуют<export>).

<template<><template-parameter-list><>>

Нет.

Это не поддерживается, поскольку локальные функции реализуются с использованием локальных классов иC++03локальные классы не могут быть шаблонами.

<explicit>

Нет.

Это не поддерживается, потому что локальные функции не являются конструкторами.

<inline>

Да.

Локальные функции могут быть указаны<inline>, чтобы повысить вероятность того, что компиляторыC++03могут оптимизировать время выполнения вызова локальной функции (но<inline>локальные функции не могут быть переданы в качестве параметров шаблона на компиляторахC++03, см. разделAdvanced Topics).

<extern>

Нет.

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

<static>

Нет.

Это не поддерживается, поскольку локальные функции не являются функциями членов.

<virtual>

Нет.

Это не поддерживается, поскольку локальные функции не являются функциями членов.[a]

<result-type>

Да.

Это поддерживается (см. разделУчебник).

<function-name>

Да.

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

<parameter-list>

Да.

Это поддерживается, а также поддерживает<auto>и<register>классификаторы хранения, параметры по умолчанию и связывание переменных в объеме (см. разделыУчебникиРасширенные темы).

Трейлинг<const>квалификация

Нет.

Это не поддерживается, поскольку локальные функции не являются функциями членов.

Трейлер<volatile>квалификация

Нет.

Это не поддерживается, поскольку локальные функции не являются функциями членов.

[a]Обоснование.Можно сделать так, чтобы класс локальных функций наследовал от другого класса локальных функций. Однако эта функция «наследования» не реализована, потому что она не казаласьполезной, учитывая, что локальные функции могут быть связаны друг с другом, таким образом, они могут просто вызывать друг друга напрямую, не возвращаясь к динамическому связыванию или вызовам базовой функции.

Operators

Местные функции не могут быть операторами. Наименование локальной функции<operator...>будет генерировать ошибку времени компиляции.<()>

Например, следующий код не компилируется (см. также<operator_error.cpp>):

bool BOOST_LOCAL_FUNCTION(const point& p, const point& q) {
    return p.x == q.x && p.y == q.y;
} BOOST_LOCAL_FUNCTION_NAME(operator==) // Error: Cannot use `operator...`.

Goto

Можно прыгать с 409 внутри местного функционального тела. Например, следующие компиляции (см. также<goto.cpp>):

int error(int x, int y) {
    int BOOST_LOCAL_FUNCTION(int z) {
        if(z > 0) goto success; // OK: Can jump within local function.
        return -1;
    success:
        return 0;
    } BOOST_LOCAL_FUNCTION_NAME(validate)
    return validate(x + y);
}

Тем не менее, невозможно перепрыгнуть с<goto>из локального функционального тела на ярлык, определенный в прилагаемой области. Например, следующее не компилируется (см. также<goto_error.cpp>):

int error(int x, int y) {
    int BOOST_LOCAL_FUNCTION(int z) {
        if(z <= 0) goto failure;    // Error: Cannot jump to enclosing scope.
        else goto success;          // OK: Can jump within local function.
    success:
        return 0;
    } BOOST_LOCAL_FUNCTION_NAME(validate)
    return validate(x + y);
failure:
    return -1;
}



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

Авторы не считают использование макроса<WITH_DEFAULT>более читаемым и предпочитают использовать ключевое слово<default>напрямую. Кроме того,<WITH_DEFAULT>должен быть определен по-разному для компиляторов без вариадных макросов<#defineWITH_DEFAULT (default)>, поэтому он может быть определен только программистами на основе синтаксиса, который они решили использовать (см. разделNo Variadic Macros).

ОбоснованиеЭто ограничение связано с тем, что эта библиотека использует конкатенацию токенов препроцессора<##>для проверки макро-параметров (для различения параметров функций, связанных переменных и т. Д.), И препроцессор C++ не позволяет конкатенировать неалфавитные токены.

<,>Препроцессор всегда интерпретирует незавернутые запятые как разделяющие макропараметры. Таким образом, в этом случае запятая будет указывать препроцессору, что первый макропараметр<const std::map<std::tring>, второй макропараметр<size_t>& m>и т.д. вместо прохождения<conststd::map<std::string,size_t>&m>в качестве одного макропараметра.

<()>Обоснование.Названия типов<result_type>и<arg><N><_type>следуют заBoost. TypeTraits— условное обозначение функциональных признаков.

<()>В примерах этой документации, связанные переменные, параметры функции и тип результата указаны в этом порядке, потому что это порядок, используемыйC++11 лямбда функций. Однако библиотека принимает связанные переменные, параметры функции и тип результата в любом порядке.

Обоснование.Эта библиотека использует вызов косвенной функции через указатель функции, чтобы передать локальную функцию в качестве параметра шаблона (см. разделРеализация). Ни один компилятор еще не был замечен способным вводить вызовы функций, когда они используют такие вызовы указателей непрямых функций. Поэтому встроенные локальные функции не используют такой косвенный вызов указателей функций (поэтому они с большей вероятностью будут оптимизированы), но из-за этого они не могут быть переданы в качестве параметров шаблона. Непрямой вызов указателя функции необходим наC++03, но он не нужен наC++11(см.[N2657]иBoost.Config's<BOOST_NO_CXX11_LOCAL_CLASS_TEMPLATE_PARAMETERS>), таким образом, эта библиотека автоматически генерирует локальные вызовы функций, которые могут быть встроенными в компиляторыC++11(даже когда локальная функция не объявлена встроенной).

Обоснование.Это ограничение связано с тем, что глобальный функтор, используемый для передачи локальной функции в качестве параметра шаблона (и в конечном итоге возвращенный за пределы области деклараций), не знает локального имени функции, поэтому локальное имя функции, используемое для рекурсивного вызова, не может быть установлено в глобальном функторе. Это ограничение вместе с предотвращением возможности включения являются причинами, по которым локальные функции не являются рекурсивными, если программисты явно не объявят их<recursive>.

<<>>Классификатор хранения<auto>является частью стандартаC++03и поэтому поддерживается этой библиотекой. Однако значение и использование ключевого слова<auto>изменилось вC++11. Поэтому используйте классификатор хранения<auto>с обычной осторожностью, чтобы избежать написанияC++03кода, который может не работать наC++11.

<[]>Обоснование.Макросы<BOOST_LOCAL_FUNCTION>и<BOOST_LOCAL_FUNCTION_TPL>внутренне используют<__LINE__>для генерации уникальных идентификаторов. Поэтому, если эти макросы будут расширены более чем вовремя на одной линии, сгенерированные идентификаторы больше не будут уникальными и код не будет компилироваться. (Это ограничение не распространяется на MSVC и другие компиляторы, которые предоставляют нестандартный макрос<__COUNTER__>.) Обратите внимание, что макрос<BOOST_LOCAL_FUNCTION_NAME>всегда может быть расширен несколько раз на одной линии, потому что уникальное локальное имя функции (а не<__LINE__>) используется этим макросом для генерации уникальных идентификаторов (так что нет необходимости в макросе<BOOST_LOCAL_FUNCTION_NAME_ID>).

<()>Поскольку существуют ограничения на набор токенов, которые может сопрягать препроцессор, и поскольку не все компиляторы правильно реализуют эти ограничения, в целом рекомендуется указывать уникальные идентификаторы как комбинацию буквенно-цифровых токенов.

<()>Обоснование.Это связано с тем, что локальное имя функции должно быть действительным локальным переменным именем (локальная переменная, используемая для удержания локального функтора), а операторы не могут использоваться в качестве локальных переменных имен.


PrevUpHomeNext

Статья Advanced Topics раздела Chapter 1. Boost.LocalFunction 1.0.0 Chapter 1. Boost.LocalFunction 1.0.0 может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 1. Boost.LocalFunction 1.0.0 ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-20 02:24:22/0.039369106292725/1