access vtkSmartPointer member variable

Hi All,

I have a question regarding proper use of vtkSmartPointer as a member variable. As I understand it, to prevent the smart pointer from going out of scope, it can be set as a member variable so that it is not deleted until the class object is deleted. As in my example below, accessing the smart pointer in class A is not a problem. However, in class B trying to explicitly access the smart pointer leads to segfaults unless I specifically run the class A method from class B ( see getSomethingFromA()).

Why doesn’t a->getSomething() work?

class A
    {
    A()
    {
        doSomething();
    }

void doSomething()
{   // read from file and copy to poly data
    // ...
    
    sp->ShallowCopy(reader->GetOutput());

    // everything with sp is ok here .. reference count = 1
    sp->PrintSelf(std::cout, vtkIndent(2));
}

vtkSmartPointer<vtkObject> getSomething()
{
    return this->sp;
}

vtkSmartPointer<vtkPolyData> sp = vtkSmartPointer<vtkPolyData>::New();
}


class B
{
B()
{
    a = new A;
};

void getSomethingFromA()
{
    a->sp = a->doSomething(); // this works to create a new reference
    a->sp->PrintSelf(std::cout, vtkIndent(2));  // prints ref count = 2

    vtkSmartPointer<vtkPolyData> spB = vtkSmartPointer<vtkPolyData>::New();
    spB = a->getSomething(); // this does not work, and segfaults. 
}

A *a;
}

Thanks,
Ryan

1 Like

This looks very odd and I’m surprised it even compiles. That implicit cast from a vtkSmartPointer<vtkObject> into a vtkSmartPointer<vtkPolyData> looks fishy.

@RobertMaynard @brad.king Thoughts?

I tried to reproduce this issue with the following reduced test:

#include "vtkFloatArray.h"
#include "vtkSmartPointer.h"

struct A
{
  A() { }

  vtkSmartPointer<vtkObject> getSomething() { return this->sp; }

  vtkSmartPointer<vtkFloatArray> sp = vtkSmartPointer<vtkFloatArray>::New();
};

int main(int, char*[])
{
  A a;
  vtkSmartPointer<vtkFloatArray> spB = vtkSmartPointer<vtkFloatArray>::New();
  spB = a.getSomething(); // this does not work, and segfaults.
  return 0;
}

As I expected the line spB = a.getSomething(); doesn’t compile on my machine as up the inheritance tree is not allowed ( as seen below ). This happens as vtkObject isn’t a subclass of vtkFloatArray. What you want to do is spB = vtkFloatArray::SafeDownCast(a.getSomething());.

... required from ‘vtkSmartPointer<ArrayType>& vtkSmartPointer<ArrayType>::operator=(const vtkSmartPointer<U>&) [with U = vtkObject; T = vtkFloatArray]’
 error: static assertion failed: Argument type is not compatible with vtkSmartPointer<T>'s T type.

Thanks for replying and providing an example. You are correct it is fishy, because I mistakenly wrote the wrong type.

It should be
vtkSmartPointer<vtkPolyData> getSomething() { return this->sp;}

So, there is no cast needed.
@RobertMaynard could you let me know what you think now that I have changed the type?

Thanks,
Ryan

We need a full example that reproduces the problem. Without that we can only make guesses.

My suspicion is that you’ve forgot to call reader->Update() before calling reader->GetOutput().

Another thing that I’ve noticed is not an error just an unnecessary object creation and deletion:

vtkSmartPointer<vtkPolyData> spB = vtkSmartPointer<vtkPolyData>::New();
spB = a->getSomething();

should be just this:

vtkSmartPointer<vtkPolyData> spB = a->getSomething();

(you created a new polydata but in the next line you reassigning the only smartpointer that referenced it, which deleted the polydata)

Another inefficiency is to use vtkSmartPointer to return the pointer from class A. You could return a simple pointer, as the ownership of the polydata is clear (it is being owned by object of class A, nothing will delete it while the other class is using it).

Ownership would be even clearer by returning vtkPolyData& (or even vtkPolyData const&!), but VTK is in very backwards in that respect, so normal C++ reference idioms just don’t work in its codebase.

1 Like

Thanks for the replies and insight everyone. With the full example below I was able to access the smart pointer as expected. Boiling it down to this example helped, and I was able to expand upon it my code. The original issue was just a messy implementation, and not cleaning out all the project headers properly. Hope this helps someone down the line.

TestSmartPointer.hpp

    #ifndef TEST_SP
    #define TEST_SP

    #include "vtkPolyData.h"
    #include "vtkSmartPointer.h"
    #include "vtkPolyDataReader.h"

    struct Handler
    {
    Handler() 
    {
        reader->SetFileName("temp_treatmentVolume.vtk");
        reader->Update();

        sp = reader->GetOutput();
    }

    vtkSmartPointer<vtkPolyData> getSomething() { return this->sp; }

    vtkSmartPointer<vtkPolyData> sp = vtkSmartPointer<vtkPolyData>::New();
    vtkSmartPointer<vtkPolyDataReader> reader = vtkSmartPointer<vtkPolyDataReader>::New();
    };

    #endif // TEST_SP

Model.cpp

#include "testSmartPointer.hpp"

class Model 
{
    public:
        Model();
        ~Model();

        Handler *handler;
};

Model::Model()
{
    handler = new Handler();
    
    std::cout << "Model-> Num Points: " << handler->sp->GetNumberOfPoints() << std::endl;
}

Model::~Model(){ delete handler;}


int main(int, char*[])
{
    Model * model = new Model();

    std::cout << "Main -> Num Points: " << model->handler->sp->GetNumberOfPoints() << std::endl;

    model->handler->sp->PrintSelf(std::cout, vtkIndent(2));
    delete model;

    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(testSmartPointer)

find_package(VTK COMPONENTS

vtkCommonCore

vtkIOLegacy

)

include(${VTK_USE_FILE})

# Set your files and resources here

set( Srcs testSmartPointer.hpp Model.cpp)

include_directories(

${CMAKE_CURRENT_BINARY_DIR}

${CMAKE_CURRENT_SOURCE_DIR}

)

add_executable(testSP ${Srcs}) # ${Hdrs} ${UI_Srcs} ${MOC_Hdrs})

target_link_libraries(testSP ${VTK_LIBRARIES})