[Insight-developers] IRIX64-6.5-CC-n32-Continuous errors and
CastImageFilter
Brad King
brad . king at kitware . com
Mon, 5 Aug 2002 18:34:19 -0400 (EDT)
Aljaz,
> I'm confused... Is this part of C++ specifications or a 'feature' of SGI
> compilers?
It is standard C++, SGI seems to be the only compiler that enforces this
behavior.
>From paragraph 14.7.3/6 in the C++98 standard:
"If a template, a member template, or the member of a class template is
explicitly specialized then that specialization shall be declared before
the first use of that specialization that would cause implicit
instantiation to take place, in every translation unit in which such a use
occurs; no diagnostic is required."
Since no diagnostic is required, most platforms don't report this problem.
SGI is kind enough to notice the problem and complain.
Here is the reason it is standard:
foo.h:
template <typename T>
struct A { /* Generic implementation. */ };
foo1.cxx:
A<int> a1; // Instantiates A<int> from primary template in foo.h
foo2.cxx:
template <>
struct A<int> { /* Integer implementation. */ };
A<int> a2; // Uses specialization.
foo3.cxx:
template <> struct A<int>;
A<int> a3; // Uses specialization. Linker will find symbols.
Now consider what happens when foo1.o, foo2.o, and foo3.o are linked.
There are two definitions of the symbols from A<int>, and they are
different. This is because foo1 uses an instantiation of the primary
template, while foo2 and foo3 use the speicalization. This will confuse
the linker. If instead the forward-declaration of the specialization were
in foo.h, all code would use the same symbols. I can go into more detail
about linkers and weak v. strong symbols if you want.
> It seems that on all other platforms the compiler is able to
> automatically find the class specializations provided by the code in
> *.cxx file.
You've gotten lucky so far. The current situation doesn't involve all the
components of the above example, but it could easily do so in the future.
> Furthermore, forward declaring the template parameter doesn't work. The
> class used as a template parameter has to be completely declared (at
> least the way class GenerateMesh is implemented now, because
> GenerateMesh references one of its typedefs...).
True, I didn't notice that. You can, however, avoid the typedef lookup by
simply using "const ElementType*" instead of "ElementType::ConstPointer".
This is actually preferable as it is ITK standard to pass raw pointers as
function arguments instead of smart pointers. In this case it won't
change anything since ElementType::ConstPointer isn't a smart pointer
anyway.
> I implemented this class as templated one so that later, when new
> elements are introduced, it should be quite simple to write functions
> that generate various types of meshes from these new elements in a
> consistently way. Basically you just need to provide the implementation
> (specialization) of the member functions of GenerateMesh class for the
> specific element type without the need for changing the existing library
> code (file itkFEMGenerateMesh.h) and enforcing consistent syntax.
>
> The additional declaration of each specialized function in file
> itkFEMGenerateMesh.h seems to defeat the purpose of using manual
> specialization in the first place. Do you have any ideas, how these
> things could be implemented in some other way?
This approach is known as a "trait". Traits are used when most of the
implementation of a class over two types is the same, but small sections
differ. The code for sections that differ is looked up through a "trait"
class that can be programmed on a per-type basis using template
specialization.
Your implementation of traits is different from the usual way we use in
ITK. In this case, it looks like GenerateMesh is basically a trait class
in itself. You might look at writing it this way:
itkFEMGenerateMesh.h:
// Empty primary template:
template <typename TElementType> class GenerateMesh;
// Specialization of entire class for Element2DC0LinearQuadrilateral:
class Element2DC0LinearQuadrilateral;
template<>
class GenerateMesh<Element2DC0LinearQuadrilateral>
{
typedef Element2DC0LinearQuadrilateral ElementType;
typedef vnl_vector<ElementType> VectorType;
static void Rectangular(const ElementType* e0,
Solver& S,
VectorType& orig,
VectorType& size,
VectorType& Nel);
};
// Other specializations...
itkFEMGenerateMesh.cxx:
// Implement specializations:
void
GenerateMesh<Element2DC0LinearQuadrilateral>
::Rectangular(ElementType::ConstPointer e0, Solver& S, VectorType& orig,
VectorType& size, VectorType& Nel)
{
/* ... */
}
This version requires a little more code, but it is more consistent with
other traits in ITK, and will be more easily understood by most
programmers (I think).
-Brad