jeudi 24 janvier 2019

std::function undefined behaviour when creating a temporary

I'm currently implementing few abstractions to represent level-set operations for 3D objects. Basically what is described in this amazing page for GLSL shaders.

To give a brief overview, a 3D object can be described by a function that maps the R^3 domain to a scalar called level-set (or signed-distance-function). For example, for a sphere, the level-set function is defined by phi(X) = X.Norm2() - R*R where Norm2 represents the squared Euclidean norm of a vector in R^3.

So I came out with a LevelSetObject class that represents such a concept:

 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);
        }

So for example a sphere is represented by a LevelSetSphere object defined as follows:

class LevelSetSphere : public LevelSetObject
    {
    private:
        double m_R; /**< The radius of the sphere */
        SimpleVector m_C; /**< The center of the sphere */

    public:
        /** @brief Constructor
         *
         *  @param  radius      The radius of the sphere
         */
        LevelSetSphere(double radius, SimpleVector center);
    };
}

LevelSetSphere::LevelSetSphere(double radius, SimpleVector center) :
    m_R(radius), m_C(center) {

        m_SDF = [this](double x, double y, double z) {
            // Let's reposition with the center
            x = x - m_C.X();
            y = y - m_C.Y();
            z = z - m_C.Z();

            SimpleVector X(x, y, z);
            return X.Norm2() - m_R*m_R;
        };
    }

Now I wanted to define few operators between LevelSetObjects. For example the union operator:

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;
    }

But I'm experiencing bad memory access when I create a temporary due to, for example, a triple sum (while if I sum the two objects separately as in the commented case, no memory leak is spotted using Valgrind and not SIGSEV):

int main(int argc, char* argv[]) {
    Volume domain(50, 50, 50, SimpleVector(4, 4, 4));

    // 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);



}

What I suppose is that in the operator+ definition, the std::function keep a reference to other that becomes a dangling pointer when I actually call m_SDF because a temporary is created during the triple sum. I also tried to change the signature of operator+ to operator+(const LevelSetObject other), so passing by copy, but the outcome is the same.

Where am I failing? :)

Aucun commentaire:

Enregistrer un commentaire