• Авторизация


Частичное применение функций в C++ 28-10-2009 00:58 к комментариям - к полной версии - понравилось!


Карринг в Python я уже делал, и это было несложно: функции, являющиеся объектами первого рода и сборщик мусора в наличии.
Теперь я спустился ещё ниже по уровню абстракции, и сделал это же на C++. Было непросто, кода получилось много, и он не универсален, но тем не менее, возможность доказана.
Основная идея заключается в том, что хоть функции не являются объектами первого класса, их можно сымитировать экземплярами класса с перегруженным методом "курглые скобки". Но есть одна сложность: нет сборщика мусора, поэтому нельзя тупо плодить объекты, и делать вид, будто не мы насрали в память. А сама концепция частичного применения функций предполагает возможность возникновения объектов без ссылок на них. Поэтому пришлось использовать auto_ptr и делать вид, будто мы работаем не с объектом, а с указателем на него. Это всё жутко усложнило, добавило кучу скобок к записи, которая должна быть интуитивно понятна, но ничего не поделаешь (в рамках STL, не входящие в стандарт библиотеки сборщиков мусора ещё как имеются).
Итак, реализуем функцию add от двух агрументов. Условие: передавать можно ноль, один или два аргумента. В первых вух слачаях функция возвращает частично применённый экзмепляр себя, в последнем -- int с результатом. И опять натыкаемся на грабли C++ -- статическая типизация. Пришлось всегда возвращать себя, но для случая, когда применено два аргумента, определить преобразование типа к int.
Уже применённые агрументы и флаг, определяющий, сколько их было применено хранятся просто как члены класса.
В итоге, код получился такой:
#include
#include

using namespace std;

class Add
{
private:
int whoiam, a, b;
public:
Add()
{
whoiam = 0;
a = 0;
b = 0;
}
Add(int var)
{
whoiam = 1;
a = var;
b = 0;
}
Add(int vara, int varb)
{
whoiam = 2;
a = vara;
b = varb;
}
auto_ptr operator()(int var)
{
switch(whoiam)
{
case 0:
return auto_ptr (new Add(var));
case 1:
return auto_ptr (new Add(a, var));
default:
throw 10;
}
}

operator int()
{
if(2 != whoiam)
throw 11;
else
return a+b;
}
friend ostream& operator <<(ostream& os, Add aa);
};
ostream& operator<<(ostream& os, Add aa)
{
switch(aa.whoiam)
{
case 0:
os << "Functor Add";
break;
case 1:
os << "Functor Add(" << aa.a << ")";
break;
case 2:
os << (int)aa;
break;
default:
os << "WTF?";
}
return os;
}

int main()
{
auto_ptr add(new Add);
cout << *add << endl;
auto_ptr inc = (*add)(1);
cout << *inc << endl;
cout << *((*inc)(2)) << endl;
int a = *((*inc)(3));
cout << a << endl;
int b = *(*((*add)(5)))(6);
cout << b << endl;
return 0;
}
Быдлокод жуткий, но ничего лучшего мне в голову не пришло, а отдельный класс для исключений и свои вариации на тему auto_ptr, чтобы избежать использования лишних разадресаций, мне было просто лень писать. В перспективе можно это сделать, а также написать универсальную обёртку для уже существующих функций, которая превращает их в объекты первого класса. Но я подозреваю, что это уже давно сделано и гораздо лучше. Мне было интересно сделать самому.
Что дальше? Писать на Си? Пожалуй, можно. Вот только перегрузки операторов там нет, функция-обёртка всё равно понадобится. Значит, придётся делать макросы, которые позволят делать вид, будто это простая функция.
А можно и на ассемблере. Там это как раз очень прозрачно делается. Ведь когда пишешь на ассемблере, ничто не мешает тупо в рантайме генерировать новый код функции, с применёнными аргументами. Вот только проблема: надо заставить ОС исполнять код из динамической памяти, иначе не сработает.
вверх^ к полной версии понравилось! в evernote


Вы сейчас не можете прокомментировать это сообщение.

Дневник Частичное применение функций в C++ | Positron - Проекция мысли | Лента друзей Positron / Полная версия Добавить в друзья Страницы: раньше»