![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
ExamplesBoost , Chapter 1. Boost.LocalFunction 1.0.0 , Chapter 1. Boost.LocalFunction 1.0.0
|
![]() | 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 |
---|---|
В общем, авторы рекомендуют использовать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< |
Этот пример адаптирован из[N2529]Функции лямбды C++11: Он связывает объект в объеме< |
В идеалелямбда-функции C++11позволяли бы связывать переменные, также используя<const&variable
>(постоянная ссылка) и<const&
>(все переменные по постоянной ссылке).
Обоснование.Локальные функции могут быть мономорфными только потому, что они реализованы с использованием локальных классов и локальные классы не могут быть шаблонами на C++ (даже вC++11).
Статья Examples раздела Chapter 1. Boost.LocalFunction 1.0.0 Chapter 1. Boost.LocalFunction 1.0.0 может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 1. Boost.LocalFunction 1.0.0 ::
реклама |