2020 Week 52 (Late December)
The following - possibly updated - changelog can be viewed as formatted
article at https://phabricator.mitk.org/w/mitk/changelog/2020.52/.
🛠 Third-party dependency changes
We upgraded quite a few dependencies this time as we are approaching the next official release of MITK in the beginning of 2021. The most notable upgrade is the replacement of the obsolete TinyXML v2.6.2 library with TinyXML-2 v8.0.0, since it induces critical API-breaking changes (see the migration guide further below).
Dependency | Old version | New version |
---|---|---|
Boost | 1.70 | 1.74 |
cpprestsdk | 2.10.10 | 2.10.16 |
CMake (minimum required version) | 3.14.5 | 3.18 |
CppUnit | 1.12.1 | 1.15.1 |
CTK | 78341aba (Dec 07, 2019) | 7210c5bc (Nov 08, 2020) |
DCMQI | ea4f0809 (Jan 23, 2020) | 99192b76 (Nov 06, 2020) |
GDCM | 3.0.4 | 3.0.8 |
ITK | e53d1d94 (May 22, 2020) | 4.13.3 |
Qwt | 6.1.0 | 6.1.5 |
2.6.2 | ||
TinyXML-2 | 8.0.0 | |
✨ New features
- Improved performance for image interactions in 2d render windows
- Windows installers/packages now include shortcuts in their top-level directory to easily start MITK applications
- Windows installers do not need to include/install Microsoft Visual C++ Redistributables anymore
🐛 Bugfixes
- Fixed loading (including query/retrieve) of many DICOM files in MITK installers for Linux and macOS
- Fixed inconsistency of checkable render windows menu items
- Segmentation tools:
- Fixed clearing of preview in Fast Marching 3D tool
- Made Live Wire tool aware of active label
- Early-out when flawed CMake versions 3.19.0 or 3.19.1 are used to configure MITK (CMake v3.19.2 works again)
- Overhauled use of PACKAGE_DEPENDS for many third-party dependencies (see API-breaking changes below)
- Patched VTK v9.0.0 to compile with GCC 10
- Fixed MatchPointRegistration to compile with Clang 11
- Fixed packaging of OpenSSL binaries on Windows (e.g. used by XNAT) by introducing the two CMake cache variables MITK_OPENSSL_SSL_DLL and MITK_OPENSSL_CRYPTO_DLL to be able to explicitly set the paths of the OpenSSL binaries
- Fixed random fails of mitkRESTClientTest
- CDash script fetches tags on each update now
🔥 API-breaking changes
Minor changes
- The LayoutDesignListChanged() method of QmitkRenderWindow was renamed to UpdateLayoutDesignList()
ITK dependencies
ITK dependencies were completely redone for all modules and plugins to only include necessary dependencies and to only expose the necessary subset of those dependencies to dependers. In case of linker errors involving ITK, you probably have to explicitly add a certain ITK module dependency to your MITK module or plugin that you were infected with by other MITK modules in previous versions of MITK.
PACKAGE_DEPENDS (third-party dependencies)
PACKAGE_DEPENDS now properly supports Boost, OpenMesh, OpenMP, OpenSSL, and Python3. For example:
- PACKAGE_DEPENDS Boost is short for Boost|boost (header-only part of Boost)
- PACKAGE_DEPENDS Boost|date_time+random maps to Boost::date_time Boost::random
- PACKAGE_DEPENDS OpenMesh|Tools maps to OpenMeshTools
- PACKAGE_DEPENDS OpenMP maps to OpenMP::OpenMP_CXX
- PACKAGE_DEPENDS OpenSSL|SSL maps to OpenSSL::SSL
- PACKAGE_DEPENDS Python3|NumPy maps to Python3::NumPy
ITK, OpenGL, Poco and Qt5 dependencies are now based on targets instead of directories as well.
Also, TARGET_DEPENDS was introduced as option to bypass the PACKAGE_DEPENDS mechanism by passing dependencies directly to target_link_libraries(). The option was added to:
- mitk_create_module()
- mitk_create_plugin()
- mitk_create_executable()
- MITK_CREATE_MODULE_TESTS()
- mitkFunctionCreateCommandLineApp()
- mitkFunctionCreateMatchPointDeployedAlgorithm()
Removal of Biophotonics and Photoacoustics
Both Biophotonics and Photoacoustics were experimental components of MITK that were no longer maintained for quite a while. They are removed without replacement.
Removed modules
- mitkBiophotonics
- MitkBiophotonicsHardware
- MitkSpectroCam
- MitkPhotoacousticsAlgorithms
- MitkPhotoacousticsHardware
- MitkPhotoacousticsLib
- MitkUSHardwareDiPhAS
Removed plugins
- org.mitk.gui.qt.lasercontrol
- org.mitk.gui.qt.photoacoustics.imageprocessing
- org.mitk.gui.qt.photoacoustics.pausmotioncompensation
- org.mitk.gui.qt.photoacoustics.pausviewer
- org.mitk.gui.qt.photoacoustics.simulation
- org.mitk.gui.qt.photoacoustics.spectralunmixing
- org.mitk.gui.qt.spectrocamrecorder
Other removed classes
- mitk::USDiPhASDeviceCustomControls
- QmitkUSControlsCustomDiPhASDeviceWidget
TinyXML to TinyXML-2 migration guide
TinyXML package dependencies of MITK modules
mitk_create_module( - PACKAGE_DEPENDS PRIVATE tinyxml + PACKAGE_DEPENDS PRIVATE tinyxml2 )
TinyXML header file
- #include <tinyxml.h> + #include <tinyxml2.h>
TinyXML forward declarations
- class TiXmlDocument; - class TiXmlElement; - class TiXmlNode; + namespace tinyxml2 + { + class XMLDocument; + class XMLElement; + class XMLNode; + }
TinyXML class names
See the forward declarations above to get an idea of the mapping between TinyXML class names and TinyXML-2 class names.
Use auto
In most cases when traversing or iterating through an XML document, a simple type replacement with the auto keyword is enough. You don't have to care if it is an element or a node - it just works.
- TiXmlElement* elem = parentElem->FirstChildElement(); + auto* elem = parentElem->FirstChildElement();
TinyXML enumeration values
- TIXML_SUCCESS + tinyxml2::XML_SUCCESS
TinyXML class instantiation and object lifetime
TinyXML-2 objects like elements or nodes are always owned by a document instance. Do not use new or delete anymore. TinyXML-2 objects that are not part of a document are usually created on the stack like tinyxml2::Printer printer;.
- TiXmlElement* elem = new TiXmlElement("MyElement"); - parentElem->LinkEndChild(elem); // now owned by TinyXML - - TiXmlElement* anotherElem = new TiXmlElement("MyOtherElement"); - delete anotherElem; + { + tinyxml2::XMLDocument doc; + + auto* elem = doc.NewElement("MyElement"); + parentElem->InsertEndChild(elem); + + auto* anotherElem = doc.NewElement("MyOtherElement"); + } // doc is running out of scope and deletes all associated objects
To be clear, don't do:
tinyxml2::XMLElement* CreateMyElement() { tinyxml2::XMLDocument doc; auto* elem = doc.NewElement("MyElement"); return elem; }
Instead, do:
tinyxml2::XMLElement* CreateMyElement(const tinyxml2::XMLDocument& doc) { auto* elem = doc.NewElement("MyElement"); return elem; }
TinyXML methods
Search and replace
Most commonly used method names are equal. The most prominent method names to replace are:
- LinkEndChild + InsertEndChild - SetDoubleAttribute + SetAttribute
⚠️ std::string vs const char*
While TinyXML supports std::string parameters, TinyXML-2 only supports plain C-strings.
- doc.LoadFile(filename); // std::string + doc.LoadFile(filename.c_str());
Don't initialize a std::string with a const char* return type. This is already true for TinyXML but during migration we found several counterexamples. const char* can be a nullptr but initializing a std::string with nullptr provokes undefined behavior.
- std::string attr = elem->Attribute("MyAttribute"); // undefined behavior + const char* c_attr = elem->Attribute("MyAttribute"); + if (c_attr != nullptr) + { + std::string attr = c_attr; + // ... + }
⚠️ Implicit bool conversion of XMLError return types (and other return type changes)
tinyxml2::XMLError is now returned by some methods instead of bool. It is an enumeration and its tinyxml2::XML_SUCCESS value equals zero. Casting a zero integer to bool evaluates to false, resulting in inverted logic. Typical candidates during the migration were the <Load|Save>File() and Parse() methods of TiXmlDocument/tinyxml2::XMLDocument.
- TiXmlDocument doc(filename); - bool success = doc.LoadFile(); // or - TiXmlDocument doc; - bool success = doc.LoadFile(filename); // Dangerous! May still compile but logic is inverted. + tinyxml2::XMLDocument doc; + bool success = tinyxml2::XML_SUCCESS == doc.LoadFile(filename.c_str());
⚠️ Parsing XML strings and streams
You cannot stream into an XML document anymore. Instead, parse explicitly. Also notice the completely changed signature of tinyxml2::XMLDocument::Parse(). The method doesn't return the parsed contents or a nullptr anymore.
std::string xmlString(std::istreambuf_iterator<char>(xmlStream), {}); // read everything into string - TiXmlDocument doc; - xmlStream >> doc; // or - if (doc.Parse(xmlString.c_str())) // Dangerous! Still compiles but logic is inverted. - { - // ... - } + tinyxml2::XMLDocument doc; + if (tinyxml2::XML_SUCCESS == doc.Parse(xmlString.c_str())) + { + // ... + }
XML document to string/stream conversion
There were several ways to print or stream an XML document but now it boils down to:
const char* PrintXMLDocument(const tinyxml2::XMLDocument& doc) { tinyxml2::XMLPrinter printer; doc.Print(&printer); return printer.CStr(); }
XML declarations
- TiXmlDocument doc; - doc.LinkEndChild(new TiXmlDeclaration("1.0", "", "")); + tinyxml2::Document doc; + doc.InsertEndChild(doc.NewDeclaration());
⚠️ Reading and writing boolean attributes
TinyXML only supported reading boolean attributes, accepting values like 0, 1, "false", "true", "yes", and "no". Boolean values were either written implicitly or explicitly as integers or explicitly as strings ("true" or "false"). The implicit case is the dangerous one as it continues to compile with different results at runtime.
To stay compatible to current file formats, we usually want to emulate the old behavior.
- elem->SetAttribute("MyAttribute", booleanValue); // Implicitly casted to integer + elem->SetAttribute("MyAttribute", static_cast<int>(booleanValue)); // Important! Keep old behavior. elem->QueryBoolAttribute("MyAttribute", &booleanValue);
The string case mentioned above can be optimizied now as TinyXML-2 produces the same strings for boolean values.
- elem->SetAttribute("MyAttribute", booleanValue ? "true" : "false"); + elem->SetAttribute("MyAttribute", booleanValue);
Reading and writing size_t and similar types
One some platforms, size_t cannot be unambiguously implicitly casted as second parameter of SetAttribute(). Check how the value is read in (as integer in most cases) and explicitly cast accordingly.
- elem->SetAttribute("MyAttribute", vector.size()); + elem->SetAttribute("MyAttribute", static_cast<int>(vector.size())); QueryIntAttribute("MyAttribute", &value);
Public MITK API changes
Basically replace TinyXML parameter types with corresponding TinyXML-2 types. We used the opportuniy to improve const-correctness. Methods that create new instances of XML classes now have an additional document input parameter, which is used to manage the lifetime of newly created instances.
The tinyxml2.h header file is only included in implementation files. MITK header files contain forward declarations when necessary.
A few examples are:
- TiXmlElement* Serialize() override; + tinyxml2::XMLElement* Serialize(tinyxml2::XMLDocument& doc) override; - BaseProperty::Pointer Deserialize(TiXmlElement* element) override; + BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement* element) override; - static TiXmlElement* GetLabelAsTiXmlElement(Label* label); + static tinyxml2::XMLElement* GetLabelAsXMLElement(tinyxml2::XMLDocument& doc, Label* label);