Template class already defined when using ITK TestDriver

We are facing redefinition issues when using the ITK TestDriver approach for testing the RTK remote module (following a previous discussion here).

When linking the TestDriver executable to the RTK test libs, the MSVC linker complains about template classes already being defined in the test .obj file.

itkRTK-5.2.lib(itkRTK-5.2.dll) : error LNK2005: “protected: virtual __cdecl itk::InPlaceImageFilter<class itk::CudaImage<float,3>,class itk::CudaImage<float,3> >::~InPlaceImageFilter<class itk::CudaImage<float,3>,class itk::CudaImage<float,3> >(void)” (??1?$InPlaceImageFilter@V?$CudaImage@M$02@itk@@V12@@itk@@MEAA@XZ) already defined in rtkTemplateTest2.cxx.obj

The error only happens when RTK CUDA-related classes are included from a test, probably because most of those classes are inheriting from explicit templates.

A solution to the issue is to avoid explicit instantiation by using templates for CUDA classes too, but the template would be constrained to one type only using concept checking.

We also looked into using extern template declarations for the base classes, similar to ITKTransformIOBase, but it does not seem adequate because it means explicit instantiation with CudaImage in files of a module independent of ITKCudaCommon (e.g., itkInPlaceImageFilter.h for the error above).

What would be the recommended way to handle this for ITK (remote) modules? Is it a known good practice to always template classes inheriting from templated classes?

We created a small example to reproduce the problem using 2 very simple tests.

Both tests are creating an instance of a subclass of itk::InPlaceImageFilter for itk::CudaImage<float,3>.

Including RTK Cuda classes from one test throws the error reported above. What is not really clear is why including the same class in both tests works well.

Any suggestion would be much appreciated.

(Cc @simon.rit)

2 Likes

Hi @LucasGandel,
That is interesting. Do those examples compile as an independent external project (without using the CreateTestDriver mechanism)? Does it happen in other compiler other than MSVC?

With a class template, any “duplicated” instantiation is discarded at link time. But your filter is not a template (it’s a regular class that derive from explicit templates). I guess when you include the header, the compiler tries to instantiate those templates, but they are already defined in your lib. I wonder why they are not discarded.

My guess about why it fails when you include the header in one test but not in the other, is that the compiler uses the definition from your lib in the test without the header, and the generated instantiation in the test with the header, but when linking those in the test driver, it doesn’t know which one to discard.

I would try to create a MRE and ask in a C++ channel.

1 Like

Hi @phcerdan,

Thank you for your answer!

Yes the examples work well when using a combination of add_executable() and itk_add_test() (having one executable per test). And this is specific to MSVC, our understanding is that only gcc discard the duplicated instantiation.

Reproducing this with a small C++ example sounds like a good plan. I wanted to confirm whether this use case had already been solved in ITK. I’ll keep you posted here, thanks for providing good resources.

1 Like