LINUX.ORG.RU

История изменений

Исправление quasimoto, (текущая версия) :

Указатель на функцию - это просто адрес памяти. А вот указаетль на метод - это не только адрес.

Если не брать особо запущенные^W виртуальные случаи, то это обычный указатель вида RetT(*)(classT*, ...), отличается от обычного указателя на функцию только наличием classT* ─ чтобы преобразовать его в RetT(*)(...) нужно всего-то сделать «частичное применение» к this (внутри класса) или к какому-то другому объекту во вне. «Частичное применение» в C или C++ = создание локальной или анонимной функции на стеке или в куче, замыкающей объект класса.

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

Я привёл пример как указатель на произвольный метод может превращаться в указатель на обычную функцию с использованием вложенных функций gcc:

    // RetT(*)(classT*, ...) -> RetT(*)(...):
    RetT fn(...) { return this->meth(this, ...); }
    // ^ capturing `this'.

fn это локальная функция ─ выделяется на стеке (то есть сам код функции), замыкает и помнит про this. Может дёргаться из signal-like функций.

Но g++ не умеет вложенных функций, да и не стандарт это. Поэтому хочется аналогично для лямбд:

    // RetT(Class::meth*)(...) -> RetT(*)(...):
    auto fn = [&, this] (...) -> RetT { return Class::meth(...); }

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

Тот код, который дергает callback в виде функции о this ничего не знает, следовательно передавать ничего не будет.

Его не нужно передавать, он уже замкнут в callback. Настоящие замыкания в функциональных языках (да и в C++ должны) вообще выделяются в куче. Это принципиальная особенность ─ локальные объекты, в том числе замыкания, линейны, то есть на них всегда одна ссылка, их можно выделять на стеке (как это принято в C++ для локальных объектов). Возвращаемые локальные объекты (и замыкания) нелинейный, на них более чем одна ссылка, поэтому они должны выделяться в куче (как это происходит с new) и как-то освобождаться при обнулении числа ссылок (memory pools / regions, ref. counting, gc).

Получается, что нужна такая цепочка

Это я изначально назвал «костыли». Хотя подразумевал совет из C++ FAQ создать глобальный (!) указатель на объект Class, выставлять его в конструкторе и писать статическую обвёртку вокруг Class::handler использующую этот глобальны указатель. В случае с signal ещё ничего, так как обычно объект такого класса и так единственен. Но не всегда это так.

Вообще, изначально я хотел std::to_plain_pointer(Class::meth, this) который сам бы всё разруливал. Это частный случай частичного применения (есть же в С++ <functional>, в конце концов?).

Исходная версия quasimoto, :

Указатель на функцию - это просто адрес памяти. А вот указаетль на метод - это не только адрес.

Если не брать особо запущенные^W виртуальные случаи, то это обычный указатель вида RetT(*)(classT*, ...), отличается от обычного указателя на функцию только наличием classT* ─ чтобы преобразовать его в RetT(*)(...) нужно всего-то сделать «частичное применение» к this (внутри класса) или к какому-то другому объекту во вне. «Частичное применение» в C или C++ = создание локальной или анонимной функции на стеке или в куче, замыкающей объект класса.

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

Я привёл пример как указатель на произвольный метод может превращаться в указатель на обычную функцию с использованием вложенных функций gcc:

    // RetT(*)(classT*, ...) -> RetT(*)(...):
    RetT fn(...) { return this->meth(this, ...); }
    // ^ capturing `this'.

fn это локальная функция ─ выделяется на стеке (то есть сам код функции), замыкает и помнит про this. Может дёргаться из signal-like функций.

Но g++ не умеет вложенных функций, да и не стандарт это. Поэтому хочется аналогично для лямбд:

    // RetT(Class::meth*)(...) -> RetT(*)(...):
    auto fn = [&, this] (...) -> RetT { return Class::meth(...); }

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

Тот код, который дергает callback в виде функции о this ничего не знает, следовательно передавать ничего не будет.

Его не нужно передавать, он уже замкнут в callback. Настоящие замыкания в функциональных языках (да и в C++ должны) вообще выделяются в куче. Это принципиальная особенность - локальные объекты, в том числе замыкания, линейны, то есть на них всегда одна ссылка, их можно выделять на стеке (как это принято в C++ для локальных объектов). Возвращаемые локальные объекты (и замыкания) нелинейный, на них более чем одна ссылка, поэтому они должны выделяться в куче (как это происходит с new) и как-то освобождаться при обнулении числа ссылок (memory pools / regions, ref. counting, gc).

Получается, что нужна такая цепочка

Это я изначально назвал «костыли». Хотя подразумевал совет из C++ FAQ создать глобальный (!) указатель на объект Class, выставлять его в конструкторе и писать статическую обвёртку вокруг Class::handler использующую этот глобальны указатель. В случае с signal ещё ничего, так как обычно объект такого класса и так единственен. Но не всегда это так.

Вообще, изначально я хотел std::to_plain_pointer(Class::meth, this) который сам бы всё разруливал. Это частный случай частичного применения (есть же в С++ <functional>, в конце концов?).