Replace unsafe C functions Proposal

So after some discussion of how this can go, some things that came up:

Compatibility

So vtkStdString is on my list of things to go, but we can use its drawback to help bridge the compatibility here. It has an implicit const char* conversion operator (very dangerous, but no worse than C pointers anyways). We can use this to our advantage here. Note that classes that use nullptr as some magic value would need something like vtkOptionalString with convenience methods to convert to nullptr if it is none. Converting a char* member to vtkStdString can be kept compatible with:

Old code:

vtkSetStringMacro(Member);
// virtual void SetMember(const char* _arg) { … }
vtkGetStringMacro(Member);
// virtual char* GetMember() { … }

char* Member;

New code:

nullptr is invalid:

void SetMember(const char* _arg) { // Need to handle `nullptr` callers
    if (_arg) {
        this->SetMember(vtkStdString(_arg));
    }
}
void SetMember(vtkStdString _arg); // impl in C++ code (see below)
vtkStdString const& GetMember() const { return this->Member; }

vtkStdString Member;

// --------------

void SetMember(vtkStdString _arg) {
    if (_arg != this->Member) {
        this->Member = std::move(_arg);
        this->Modified();
    }
}

nullptr is valid:

void SetMember(vtkOptionalString _arg); // impl in C++ code (same body as above)
vtkOptionalString const& GetMember() const { return this->Member; }

vtkOptionalString would be another interim class to help bridge the gap here which just turns nullptr into std::none_t. It’s implicit const char* conversion would similarly be deprecated someday (just like vtkStdString's).

vtkSetGet macros

I find these macros to be more noise than useful. About the only thing they do is:

  • avoid typos
  • ensure Modified() is called properly in setters

Other than that, I see no benefit. I think that unit tests are far better for ensuring such things. The benefits:

  • virtual would no longer be used for these things
  • getters could be const
  • getters could return const& instead of forcing a copy
  • setters could std::move for non-POD types (allowing the caller to decide if memory is reused instead of pessimizing and forcing a copy even when the caller doesn’t need the string anymore)
  • headers lose a lot of unnecessary code that triggers a full rebuild when vtkSetGet is changed
  • the member name is no longer pinned to the method name (allowing for more PIMPL usage)

The inline-ness is largely useless for the setters because VTK doesn’t support devirtualization of Modified calls (the use of factory constructors hides necessary knowledge even for vtkNew<vtkObject> obj; obj->SetSomething();), so the code is the same for every single caller and any actual inlining is of no benefit (it just inflates the caller’s code size).