Приветствую сообщество!
Я наткнулся на возможность сделать в С++ что-то похожее на объявление функций внутри функций. Выглядит это вот так:
В приведенном примере внутри метода main изготавливается вложенный «метод» с названием make_hello и затем вызывается с параметром «Vasiliy Pupkin». Разумеется, на экран будет выведено
К сожалению, перетащить название вверх у меня не получилось.
Сделано это, конечно же, на макросах.
В C++ все-таки нету вложенных функций, поэтому нам придется эмулировать их синтаксис чем-либо еще. Поэтому зададимся другим вопросом: что именно выглядит так же, как функция, но не функция?
Ответа целых два: это, во-первых, вызов конструктора класса без с созданием объекта «в пустоту»:
К сожалению, есть два существенных «но», которые не позволяют использовать приведенный пример на практике:
1. Мы создаем по одному новому объекту на каждый вызов функции, что не есть хорошо. А ну как у меня цикл на много вызовов? Накладные расходы можно стерпеть на факт декларации такой штуковины, но никак не на использование.
2. Visual Studio со включенной оптимизацией компилятора просто-напросто вырежет создание inline_function() как бесполезное. Логика компилятора понятна (все равно этот создаваемый объект никто не будет использовать, так зачем его создавать?) но представляет опасность.
Однако есть еще штука под названием «переопределение операторов» — и мы можем переопределить двойные скобочки (по умному переопределенный оператор () называется «функтором», во как):
В этом примере уже почти все хорошо, кроме того, что класс можно сделать и анонимным, чтобы попусту не трепать имя вида
Итак, цель — в принципе — достигнута: у нас есть способ изолировать кусок кода внутри одного метода так, чтобы не подпортить внешнюю зону видимости. Но есть один минус: во всех приведенных примерах — на мой вкус — слишком много букв. Поэтому мы разделим наш код на три части:
Первую и вторую части можно запихнуть в два макроса, дав им какие-нибудь красивые имена. Например,
При помощи этих макросов код «вложенной функции» значительно упрощается на вид (хоть и выглядит не в С++ стиле):
К сожалению, все портится, если попробовать задать не один параметр у функции, а хотя бы два. В этом случае компилятор нажалуется, мол, в макросе inline_function всего ОДИН параметр и баста. Проблему можно решить, использовав макрос __VA_ARGS__ (который, хоть и не входит в стандарт, но поддерживается всеми реально используемыми компиляторами).
Чуть сложнее, но все еще приемлимо будет выглядеть функция с двумя параметрами:
В заключение отмечу, что у этих макросов не предусмотрено возвращаемого значения. Разумеется, его можно и очень просто «пробросить наверх», так что это я оставляю читателям.
P.S. Вообще-то техника изготовления вложенных классов стара как мир и я, конечно же, не открыл ничего нового. Но тем не менее, как мне кажется, такая статья полезна в образовательном смысле и обладает некоторой эстетической завершенностью.
Я наткнулся на возможность сделать в С++ что-то похожее на объявление функций внутри функций. Выглядит это вот так:
#include <iostream> int main() { inline_function(std::string s) { std::cout << "Hello, " << s << "!\n"; } with_name(make_hello); make_hello("Vasiliy Pupkin!"); return 0; }
В приведенном примере внутри метода main изготавливается вложенный «метод» с названием make_hello и затем вызывается с параметром «Vasiliy Pupkin». Разумеется, на экран будет выведено
Hello, Vasiliy Pupkin!.К сожалению, перетащить название вверх у меня не получилось.
Сделано это, конечно же, на макросах.
Примеры того же самого без макросов
В C++ все-таки нету вложенных функций, поэтому нам придется эмулировать их синтаксис чем-либо еще. Поэтому зададимся другим вопросом: что именно выглядит так же, как функция, но не функция?
Ответа целых два: это, во-первых, вызов конструктора класса без с созданием объекта «в пустоту»:
#include <stdio.h> int main() { // Вместо функции мы изготовим класс с конструктором class inline_function { public: // Вызов конструктора будет синтаксически неотличим от вызова вложенного метода inline_function() { printf("Hello, hell!\n"); } }; // создание объекта и вызов конструктора inline_function(); return 0; }
К сожалению, есть два существенных «но», которые не позволяют использовать приведенный пример на практике:
1. Мы создаем по одному новому объекту на каждый вызов функции, что не есть хорошо. А ну как у меня цикл на много вызовов? Накладные расходы можно стерпеть на факт декларации такой штуковины, но никак не на использование.
2. Visual Studio со включенной оптимизацией компилятора просто-напросто вырежет создание inline_function() как бесполезное. Логика компилятора понятна (все равно этот создаваемый объект никто не будет использовать, так зачем его создавать?) но представляет опасность.
Однако есть еще штука под названием «переопределение операторов» — и мы можем переопределить двойные скобочки (по умному переопределенный оператор () называется «функтором», во как):
#include <stdio.h> int main() { // Вместо функции мы изготовим класс переопределенным оператором class inline_function_class { public: void operator()() { printf("Hello, world!\n"); } }; // Создадим объект только что созданного типа -- всего один раз! inline_function_class inline_function; // И вызовем функтор inline_function(); return 0; }
В этом примере уже почти все хорошо, кроме того, что класс можно сделать и анонимным, чтобы попусту не трепать имя вида
inline_function_class#include <stdio.h> int main() { // Вместо функции мы изготовим класс переопределенным оператором class { public: void operator()() { printf("Hello, world!\n"); } } inline_function; // Декларация и объявление в одном флаконе // И вызовем функтор inline_function(); return 0; }
Пишем макросы
Итак, цель — в принципе — достигнута: у нас есть способ изолировать кусок кода внутри одного метода так, чтобы не подпортить внешнюю зону видимости. Но есть один минус: во всех приведенных примерах — на мой вкус — слишком много букв. Поэтому мы разделим наш код на три части:
#include <stdio.h> int main() { // Вместо функции мы изготовим класс переопределенным оператором /** первая часть -- до имплементации функции, которая заранее известна, если не говорить о параметрах: **/ class { public: void operator()(/* а тут ведь могут быть и параметры*/) { /**вторая часть -- собственно, тело функции, которое будет писать человек **/ printf("Hello, world!\n"); /** Третья часть -- завершающая часть класса **/ } } inline_function; // Декларация и объявление в одном флаконе /** =============================== **/ // И вызовем функтор inline_function(); return 0; }
Первую и вторую части можно запихнуть в два макроса, дав им какие-нибудь красивые имена. Например,
#define inline_function(params) \ class \ { \ public: void operator() (params)\ {\ #define with_name(value) \ }\ } value; #define with_params(...) __VA_ARGS__ // А это-то зачем тут? Читай ниже.
При помощи этих макросов код «вложенной функции» значительно упрощается на вид (хоть и выглядит не в С++ стиле):
int main() { inline_function(char * name) { printf("Hello, %s!\n",name); } with_name(hello) hello("Pupkin"); }
К сожалению, все портится, если попробовать задать не один параметр у функции, а хотя бы два. В этом случае компилятор нажалуется, мол, в макросе inline_function всего ОДИН параметр и баста. Проблему можно решить, использовав макрос __VA_ARGS__ (который, хоть и не входит в стандарт, но поддерживается всеми реально используемыми компиляторами).
Чуть сложнее, но все еще приемлимо будет выглядеть функция с двумя параметрами:
int main() { inline_function (with_params(int a, int b)) { printf("%d+%d=%d\n",a,b,a+b); } with_name(plus); plus(2,2); }
В заключение отмечу, что у этих макросов не предусмотрено возвращаемого значения. Разумеется, его можно и очень просто «пробросить наверх», так что это я оставляю читателям.
P.S. Вообще-то техника изготовления вложенных классов стара как мир и я, конечно же, не открыл ничего нового. Но тем не менее, как мне кажется, такая статья полезна в образовательном смысле и обладает некоторой эстетической завершенностью.
