std :: function плохой доступ к памяти при создании временного - PullRequest
0 голосов
/ 24 января 2019

В настоящее время я реализую несколько абстракций для представления операций установки уровня для трехмерных объектов. В основном то, что описано в этой удивительной странице для шейдеров GLSL.

Чтобы дать краткий обзор, трехмерный объект может быть описан функцией, которая отображает домен R ^ 3 в скаляр, называемый уровнем-набором (или функцией со знаком-расстоянием). Например, для сферы функция установки уровня определяется как phi(X) = X.Norm2() - R*R, где Norm2 представляет квадратную евклидову норму вектора в R ^ 3.

Итак, я вышел с классом LevelSetObject, который представляет такую ​​концепцию:

 class LevelSetObject
    {

    using SDFFunction = std::function<double(double, double, double)>;

    protected:
        SDFFunction m_SDF;

    public:

        double SDF(double x, double y, double z) const {
            return m_SDF(x, y, z);
        }

Теперь я хотел определить несколько операторов между LevelSetObject с. Например, оператор union :

LevelSetObject LevelSetObject::operator+(const LevelSetObject& other) const {
        LevelSetObject outObj;
        outObj.m_SDF = [this, other]
            (double x, double y, double z) {
                return std::min(m_SDF(x, y, z), other.m_SDF(x, y, z));
            };
        return outObj;
    }

Но я испытываю плохой доступ к памяти, когда создаю временную, например, из-за тройной суммы (хотя, если я суммирую два объекта по отдельности, как в закомментированном случае, утечка памяти не будет обнаружена с помощью Valgrind, а не SIGSEV ). LevelSetSphere является производным классом LevelSetObject, где я определяю конкретно SDF сферы (учитывая ее center и radius)

int main(int argc, char* argv[]) {

    // Generate the central sphere
    double radius = 1.0;
    SimpleVector center(2, 2, 2);
    LevelSetSphere sphere(radius, center);

    // Generate the ears spheres
    LevelSetSphere ear1(radius/2, SimpleVector(1, 1, 2));
    LevelSetSphere ear2(radius/2, SimpleVector(3, 1, 2));

    // Combine objects
    auto mickeyMouse = sphere + ear1 + ear2;
    //auto first = sphere + ear1;
    //auto mickeyMouse = first + ear2;

    // Materialize in the domain

    mickeyMouse.SDF(0.0, 0.0, 0.0);



}

Я предполагаю, что в определении operator+, std::function сохраняет ссылку на other, которая становится висячей ссылкой, когда я на самом деле вызываю m_SDF, потому что временное значение создается во время тройной суммы. Я также пытался изменить сигнатуру operator+ на operator+(const LevelSetObject other), поэтому передавал копию, но результат тот же.

Где я терплю неудачу? :)

Ответы [ 2 ]

0 голосов
/ 24 января 2019

Ваш плохой доступ к памяти вызван не переменной other, а указателем this временного объекта, выходящего из области видимости.

Это можно исправить, явно захватив переменные SDF,как это:

LevelSetObject LevelSetObject::operator+(const LevelSetObject& other) const {
        LevelSetObject outObj;
        auto& SDF=this->m_SDF;
        auto& other_SDF=other.m_SDF
        outObj.m_SDF = [SDF, other_SDF]
            (double x, double y, double z) {
                return std::min(SDF(x, y, z), other_SDF(x, y, z));
            };
        return outObj;
    }
0 голосов
/ 24 января 2019

Лямбда в производном классе записывает this в производный класс и помещает его в std::function.

базового класса.

Это рецепт для неприятностей.

Что это означает, что, по крайней мере, производный класс должен быть полностью совместимым с правилом 3 и реализовывать, по крайней мере, конструктор копирования и оператор присваивания, который тщательно переустанавливает новый лямбда с только что захваченным this, который фактически ссылается на правильный экземпляр производного класса.

Если у вас есть std::function член класса, который захватывает свой собственный this, и класс копируется, захваченный this не обновляется автоматически для ссылки на новый экземпляр класса. C ++ не работает таким образом. Новый класс std::function this все еще ссылается на исходный экземпляр класса. И если экземпляр класса назначен из другого экземпляра класса, угадайте, что? Скопированный std::function захваченный this по-прежнему указывает на скопированный экземпляр класса.

Но на самом деле я не вижу ничего такого, что std::function делает здесь, что не может быть реализовано виртуальной функцией разнообразия сада. Просто замените m_SDF виртуальной функцией, и вся эта головная боль исчезнет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...