BlockOperator, Template Instantiation and Microsoft Compiler
Hi,
as encouraged in our last developer meeting I will try to document the issue we had recently discussed in the chat. There had been a problem report on compiling on Windows with the Microsoft Visual Studio compiler. This resulted in template instantiation errors of the following form, here as an example for the BlockOperatorBasicTest
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/functions/FunctionWrapper.hpp(179,1): error C2660: 'hyteg::BlockFunction<value_t>::enumerate': function does not take 2 arguments [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] value_t=walberla::real_t
[build] ]
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/functions/BlockFunction.hpp(264,9): message : see declaration of 'hyteg::BlockFunction<value_t>::enumerate' [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] value_t=walberla::real_t
[build] ]
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/functions/FunctionWrapper.hpp(179): message : while compiling class template member function 'void hyteg::FunctionWrapper<func_t>::enumerate(walberla::uint_t,double &) const' [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] func_t=thType
[build] ]
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/functions/GenericFunction.hpp(58): message : see reference to class template instantiation 'hyteg::FunctionWrapper<func_t>' being compiled [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] func_t=thType
[build] ]
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/operators/BlockOperator.hpp(158): message : see reference to function template instantiation 'const func_t &hyteg::GenericFunction<value_t>::unwrap<srcBlockFunc_t>(void) const' being compiled [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] func_t=thType,
[build] value_t=double,
[build] srcBlockFunc_t=thType
[build] ]
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/operators/BlockOperator.hpp(158): message : see reference to function template instantiation 'const func_t &hyteg::GenericFunction<value_t>::unwrap<srcBlockFunc_t>(void) const' being compiled [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] func_t=thType,
[build] value_t=double,
[build] srcBlockFunc_t=thType
[build] ]
[build] C:\Users\[removed]\dev\hyteg\src\hyteg/operators/BlockOperator.hpp(156): message : while compiling class template member function 'void hyteg::BlockOperator<thType,thType>::smooth_gs(const hyteg::GenericFunction<value_t> &,const hyteg::GenericFunction<value_t> &,size_t,hyteg::DoFType) const' [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] with
[build] [
[build] value_t=double
[build] ]
[build] C:\Users\[removed]\dev\hyteg\tests\hyteg\operators\BlockOperatorBasicTest.cpp(76): message : see reference to class template instantiation 'hyteg::BlockOperator<thType,thType>' being compiled [C:\Users\[removed]\dev\hyteg\build\tests\hyteg\BlockOperatorBasicTest.vcxproj]
[build] Build finished with exit code 1
The compiler is of course correct in the following respect. The BlockFunction
does not provide a version of enumerate
that accepts an offset parameter, as the other functions do. However, the BlockFunction
was also not intended to be wrapped inside a FunctionWrapper
object. So the interesting question here is: Why does the Microsoft compiler see the need to instantiate FunctionWrapper< thType >
, where thType = P2P1TaylorHoodBlockFunction< real_t >
is a child of BlockFunction<real_t>
, while neither GCC, nor Clang, nor the Intel compiler does?
My current working hypothesis is the following:
-
BlockOperator< srcBlockFunc_t, dstBlockFunc_t>
inherits fromGSSmoothable< GenericFunction< typename srcBlockFunc_t::valueType >
. -
As
GSSmoothable::smooth_gs()
is pure virtual, it needs to be implemented byBlockOperator
and must be instantiable for the given template arguments, when one attempts to generate an object of typeBlockOperator
. In the above casesrcBlockFunc_t = thType = P2P1TaylorHoodBlockFunction< real_t >
which is a child ofBlockFunction<real_t>
. -
BlockOperator::smooth_gs( GenericFunction, ...)
contains the two lines:const auto& dst_unwrapped = dst.template unwrap< srcBlockFunc_t >(); const auto& rhs_unwrapped = rhs.template unwrap< dstBlockFunc_t >();
which invoke the templated member function
GenericFunction::unwrap()
. This in turn does the following:template < typename func_t > func_t& unwrap() { auto realMe = static_cast< FunctionWrapper< func_t >* >( this ); return realMe->unwrap(); };
-
It seems that the static cast is the reason why the compiler attempts to instantiate
FunctionWrapper< thType >
and finally looks for the missingBlockFunction< real_t >::enumerate()
version. However, two questions remain:- As no object of type
FunctionWrapper< srcBlockFunc_t >
is requested, but we only have a cast to pointer, why can the template instance not remain abstract? Is that what the other compilers do? - The instantiations involved seem implicit and, thus, should be lazy. But there is no direct call to
FunctionWrapper<srcBlockFunc_t>::enumerate()
.
- As no object of type
The complexity here is high and I have no direct access to the compiler to experiment, so the analysis might by faulty. However, commenting out the inheritance and the smooth_gs()
methods in BlockOperator
fixed the problem for the Microsoft compiler. So at least no falsification.
Cheers
Marcus