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

Examples

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

В этом разделе приведены примеры использования этой библиотеки.

Комбинировать локальные функции с нестандартным выражениемвысказываниярасширения компилятора GCC, можно реализовать лямбда-функции для компиляторов GCC даже без поддержкиC++11.

[Warning]Warning

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

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

Локальные функции (только GCC)

C++11 Lambdas

int val = 2;
int nums[] = {1, 2, 3};
int* end = nums + 3;
int* iter = std::find_if(nums, end,
    GCC_LAMBDA(const bind val, int num, return bool) {
        return num == val;
    } GCC_LAMBDA_END
);

int val = 2;
int nums[] = {1, 2, 3};
int* end = nums + 3;
int* iter = std::find_if(nums, end,
    [val](int num) -> bool {
        return num == val;
    }
);

Макросы функций лямбда GCC реализуются с использованием локальных функций (см. также<gcc_lambda.hpp>):

#   define GCC_LAMBDA_(binds, params, results) \
        ({ /* open statement expression (GCC extension only) */ \
        BOOST_LOCAL_FUNCTION( \
            BOOST_PP_LIST_ENUM(BOOST_PP_LIST_APPEND(binds, \
                BOOST_PP_LIST_APPEND(params, \
                    BOOST_PP_IIF(BOOST_PP_LIST_IS_NIL(results), \
                        (return void, BOOST_PP_NIL) /* default for lambdas */ \
                    , \
                        results \
                    )\
                ) \
            )) \
        )

#define GCC_LAMBDA_END_(id) \
    BOOST_LOCAL_FUNCTION_NAME(BOOST_PP_CAT(gcc_lambda_, id)) \
    BOOST_PP_CAT(gcc_lambda_, id); \
    }) /* close statement expression (GCC extension only) */

Это возможно, поскольку выражения заявлений ССАГПЗ позволяют использовать заявления деклараций в выражениях и, следовательно, декларировать локальную функцию в выражении. Макросы автоматически определяют, поддерживает ли компиляторлямбда-функции C++11, в этом случае реализация использует нативные лямбды вместо локальных функций в выражениях высказываний GCC. Однако лямбда-функцииC++11не поддерживают постоянное связывание, поэтому лучше всего использовать только<constbind variable>(то же, что<=variable>дляC++11 лямбда-функций) и<bind&variable>(то же, что<&variable>дляC++11 лямбда-функций), потому что они имеют точно такую же семантику между локальной функцией и нативными реализациями лямбда. Кроме того, локальные функции позволяют напрямую связывать элементы данных, в то время каклямбда-функции C++11требуют доступа к элементам данных посредством связывания объекта<this>. К сожалению, кратковременные связи<&>и<=>изфункций лямбды C++11(которые автоматически связывают все переменные в объеме либо посредством ссылки, либо посредством значения) не поддерживаются этими макросами функций лямбды GCC, поскольку они не поддерживаются локальными функциями. Наконец, тип результата<return><result-type>является необязательным и предполагается<void>, когда он не указан (так же, как и сфункциями лямбды C++11).

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

Например, рассмотрим следующее утверждение, где по ошибке мы запрограммировали<operator=>вместо<operator==>:

int x = 1, y = 2;
assert(x = y); // Mistakenly `=` instead of `==`.

В идеале этот код не будет компилировать вместо этого этот пример не только компилирует, но утверждение даже проходит проверку времени выполнения и не генерируется никаких ошибок. В статье[N1613]представлена концепция конст-блока, который может быть использован для обертывания вышеприведенного утверждения и улавливания ошибки программирования во время компиляции. Аналогично, следующий код будет генерировать ошибку времени компиляции, когда<operator=>ошибочно используется вместо<operator==>, потому что и<x>и<y>сделаны константами (с использованием локальных функций) в блоке кода, выполняющего утверждение (см. также<const_block_error.cpp>):

с локальными функциями

N 1613 Конст-блоки

int x = 1, y = 2;
CONST_BLOCK(x, y) { // Constant block.
    assert(x = y);  // Compiler error.
} CONST_BLOCK_END

int x = 1, y = 2;
const {             // Constant block.
    assert(x = y);  // Compiler error.
}

Макросы постоянных блоков реализуются с использованием локальных функций (см. также<const_block.hpp>):

#define CONST_BLOCK_(variables) \
    void BOOST_LOCAL_FUNCTION( \
        BOOST_PP_IIF(BOOST_PP_LIST_IS_NIL(variables), \
            void BOOST_PP_TUPLE_EAT(3) \
        , \
            BOOST_PP_LIST_FOR_EACH_I \
        )(CONST_BLOCK_BIND_, ~, variables) \
    )

#define CONST_BLOCK_END_(id) \
    BOOST_LOCAL_FUNCTION_NAME(BOOST_PP_CAT(const_block_, id)) \
    BOOST_PP_CAT(const_block_, id)(); /* call local function immediately */

Макросы постоянных блоков реализуются с использованием локальной функции, которая связывает посредством постоянной ссылки<constbind&>все указанные переменные (так что переменные являются постоянными в блоке кода, но они не должны быть<CopyConstructible>и не выполняется дополнительная копия). Местная функция выполняет инструкцию в своем теле<assert>и называется сразу после того, как она определена. В более общем плане, постоянные блоки могут использоваться для оценки любой команды (а не только утверждений) в блоке, если все заданные переменные являются постоянными.

К сожалению, постоянные блоки не могут быть реализованы с лямбда-функциямиC++11, поскольку они не поддерживают постоянное связывание. Переменные, связанные значением с использованием лямбда-функцийC++11<variable>,<=variable>и<=>, являются постоянными, но они должны быть<CopyConstructible>и они вводят потенциально дорогостоящие операции копирования.Конечно, всегда можно ввести дополнительные постоянные переменные и связать эти переменные с лямбда-функциямиC++11, но код постоянного блока затем должен будет управлять декларацией и инициализацией этих дополнительных переменных плюс он должен будет использовать дополнительные имена переменных вместо исходных имен переменных:

int x = 1, y = 2;
const decltype(x)& const_x = x; // Constant so cannot be modified
const decltype(y)& const_y = y; // and reference so no copy.
[&const_x, &const_y]() {        // Lambda functions (C++11 only).
    assert(const_x = const_y);  // Unfortunately, `const_` names.
}();

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

Ограниченные выходы позволяют выполнять произвольный код на выходе из области охвата, и они предоставляются библиотекойBoost.ScopeExit.

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

[Note]Note

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

Следующий пример связывает<p>посредством постоянной ссылки таким образом, что эта переменная не может быть изменена в пределах тела выхода сферы действия, но она не копируется, и она будет представлять значение, которое она имеет на выходе из охватывающей области действия, а не в объявлении выхода области действия (см. также<scope_exit.cpp>):

с локальными функциями

Boost.ScopeExit

person& p = persons_.back();
person::evolution_t checkpoint = p.evolution_;
SCOPE_EXIT(const bind checkpoint, const bind& p, bind this_) {
    if (checkpoint == p.evolution_) this_->persons_.pop_back();
} SCOPE_EXIT_END

person& p = persons_.back();
person::evolution_t checkpoint = p.evolution_;
BOOST_SCOPE_EXIT(checkpoint, &p, this_) { // Or extra variable `const_p`.
    if (checkpoint == p.evolution_) this_->persons_.pop_back();
} BOOST_SCOPE_EXIT_END

Макросы выхода области действия реализуются путем прохождения локальной функции при построении объекта следующего класса (см. также<scope_exit.hpp>):

struct scope_exit {
    scope_exit(boost::function<void (void)> f): f_(f) {}
    ~scope_exit(void) { f_(); }
private:
    boost::function<void (void)> f_;
};

#   define SCOPE_EXIT(...) \
        void BOOST_LOCAL_FUNCTION(__VA_ARGS__)

#define SCOPE_EXIT_END_(id) \
    BOOST_LOCAL_FUNCTION_NAME(BOOST_PP_CAT(scope_exit_func_, id)) \
    scope_exit BOOST_PP_CAT(scope_exit_, id)( \
            BOOST_PP_CAT(scope_exit_func_, id));

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

Локальные функции можно использовать для созданияфункций Boost.Phoenix. Например (см. также<phoenix_factorial_local.cpp>и<phoenix_factorial.cpp>):

Локальные функции

Глобальный функтор

int main(void) {
    using boost::phoenix::arg_names::arg1;
    int BOOST_LOCAL_FUNCTION(int n) { // Unfortunately, monomorphic.
        return (n <= 0) ? 1 : n * factorial_impl(n - 1);
    } BOOST_LOCAL_FUNCTION_NAME(recursive factorial_impl)
    boost::phoenix::function< boost::function<int (int)> >
            factorial(factorial_impl); // Phoenix function from local function.
    int i = 4;
    BOOST_TEST(factorial(i)() == 24);      // Call.
    BOOST_TEST(factorial(arg1)(i) == 24);  // Lazy call.
    return boost::report_errors();
}

struct factorial_impl { // Phoenix function from global functor.
    template<typename Sig>
    struct result;
    template<typename This, typename Arg>
    struct result<This (Arg)> : result<This (Arg const&)> {};
    template<typename This, typename Arg>
    struct result<This (Arg&)> { typedef Arg type; };
    template<typename Arg> // Polymorphic.
    Arg operator()(Arg n) const {
        return (n <= 0) ? 1 : n * (*this)(n - 1);
    }
};
int main(void) {
    using boost::phoenix::arg_names::arg1;
    boost::phoenix::function<factorial_impl> factorial;
    int i = 4;
    BOOST_TEST(factorial(i)() == 24);      // Call.
    BOOST_TEST(factorial(arg1)(i) == 24);  // Lazy call.
    return boost::report_errors();
}

Boost.Phoenix адаптер макрос) может быть более полезным. .

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

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

Ниже приведены примеры вложенной функциональной документации GCC и запрограммированной с использованием локальных функций:

Следующие примеры взяты из различных C++ «N-бумаг» и запрограммированы с использованием локальных функций:

Файлы

Заметки

n2550_find_if.cpp

Этот пример адаптирован из[N2550]функций лямбды C++11: Он передает локальную функцию алгоритму STL<std::find_if>

.

<n2529_this.cpp>

Этот пример адаптирован из[N2529]Функции лямбды C++11: Он связывает объект в объеме<this>с локальной функцией.



В идеалелямбда-функции C++11позволяли бы связывать переменные, также используя<const&variable>(постоянная ссылка) и<const&>(все переменные по постоянной ссылке).

Обоснование.Локальные функции могут быть мономорфными только потому, что они реализованы с использованием локальных классов и локальные классы не могут быть шаблонами на C++ (даже вC++11).


PrevUpHomeNext

Статья Examples раздела 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-19 20:04:27/0.032858848571777/1