linking problems with templates

Hi people,

i'm using a templated class on my dll and it compiles and link fine, but I find that it has introduced some symbols of my templated class on the dll. It also happens with any program that includes that templated class so I have linking errors on programs that use this library because of the redefined symbols.


Answer this question

linking problems with templates

  • parmanu

    That is what I did. He is an example where it happens, I make the template and an a class that inherits from it. It worked fine on VC 6.0, the problem arises when using VC 2005

    Here is the templated class, all is done on the header.

    template <int N>
    class gfxDimensionalUnit

    {
    public:
    gfxDimensionalUnit()
    : m_Initialized(false)
    {
    }

    gfxDimensionalUnit(const gfxDimensionalUnit<N> &right)
    : m_Initialized(false)
    {
    m_Initialized = right.m_Initialized;
    memcpy(m_data,right.m_data,N*sizeof(double));
    }

    // Constructor from a vector of doubles with size elements
    gfxDimensionalUnit(const double* coord, unsigned int size)
    : m_Initialized(false)
    {
    if(size != N)
    return;

    memcpy(m_data,coord,N*sizeof(double));
    m_Initialized = true;
    return;
    }

    const gfxDimensionalUnit<N> & operator=(const gfxDimensionalUnit<N> &right)
    {
    m_Initialized = right.m_Initialized;
    memcpy(m_data,right.m_data,N*sizeof(double));
    return *this;
    }

    bool operator==(const gfxDimensionalUnit<N> &right) const
    {
    if(right.IsNull()) return false;

    for(int i = 0; i < N; i++)
    if(fabs(m_dataIdea - rightIdea) > GFX_ZERO_TOLERANCE) return false;

    return true;
    }

    bool operator!=(const gfxDimensionalUnit<N> &right) const
    {
    return !(*this == right);
    }


    // Set all N components.
    // The object is marked as initialized.
    virtual void SetComponents(double* coord, unsigned int size)
    {
    if(size == N)
    {
    memcpy(m_data,coord,N*sizeof(double));
    m_Initialized = true;
    }
    else
    m_Initialized = false;
    }

    // Get all three components. Return true if the object has
    // been initialized.
    virtual bool GetComponents(double* coord, unsigned int size)
    {
    if(size == N)
    {
    memcpy(coord,m_data,N*sizeof(double));
    return m_Initialized;
    }
    else
    return false;
    }

    // returns if the object hasn't been yet initialized
    virtual bool IsNull() const
    {
    return !m_Initialized;
    }

    // Access to one component for both read and write
    // operations.
    // Warning: if the vector is null, setting one component to
    // a new value will not set the "Initialized" variable.
    double& operator ()(int indx)
    {
    return m_data[indx];
    }

    // Access to one component for read-only operations.
    const double& operator ()(int indx) const
    {
    return m_data[indx];
    }

    // Access to one component for both read and write
    // operations.
    // Warning: if the vector is null, setting one component to
    // a new value will not set the "Initialized" variable.
    double& operator [](int indx)
    {
    return m_data[indx];
    }

    // Access to one component for read-only operations.
    const double& operator [](int indx) const
    {
    return m_data[indx];
    }

    // Get a constant pointer to the data.
    operator const double*() const
    {
    return m_data;
    }

    // Add another vector.
    friend gfxDimensionalUnit<N> operator +(const gfxDimensionalUnit<N>& vec1, const gfxDimensionalUnit<N>& vec2)
    {
    if(vec1.IsNull() || vec2.IsNull()) return gfxDimensionalUnit<N>();

    gfxDimensionalUnit<N> res(vec1);

    for(int i = 0; i < N; i++)
    resIdea += vec2Idea;

    return res;
    }

    // Substract another vector.
    friend gfxDimensionalUnit<N> operator -(const gfxDimensionalUnit<N>& vec1, const gfxDimensionalUnit<N>& vec2)
    {
    if(vec1.IsNull() || vec2.IsNull()) return gfxDimensionalUnit<N>();

    gfxDimensionalUnit<N> res(vec1);

    for(int i = 0; i < N; i++)
    resIdea -= vec2Idea;

    return res;
    }

    // increment actual vector with another vector
    virtual const gfxDimensionalUnit<N>& operator +=(const gfxDimensionalUnit<N>& vect)
    {
    //As we assign, if we add a Null vector the current vector gets also Null
    if(!vect.IsNull() && !IsNull())
    for(int i = 0; i < N; i++)
    m_dataIdea += vectIdea;
    else
    m_Initialized = false;

    return *this;
    }

    // decrements actual vector with another vector
    virtual const gfxDimensionalUnit<N>& operator -=(const gfxDimensionalUnit<N>& vect)
    {
    if(!vect.IsNull() && !IsNull())
    for(int i = 0; i < N; i++)
    m_dataIdea -= vectIdea;
    else
    m_Initialized = false;

    return *this;
    }

    // Scales the vector with the factor val
    virtual const gfxDimensionalUnit<N>& operator *=(double val)
    {
    if(!IsNull())
    for(int i = 0; i < N; i++)
    m_dataIdea *= val;

    return *this;
    }

    // Scales the vector by a factor 1/val
    virtual const gfxDimensionalUnit<N>& operator /=(double val)
    {
    if(!IsNull() && val > GFX_ZERO_TOLERANCE)
    for(int i = 0; i < N; i++)
    m_dataIdea /= val;

    return *this;
    }

    // returns the vector scaled by a value 1/val
    friend gfxDimensionalUnit<N> operator /(const gfxDimensionalUnit<N>& vec, double val)
    {
    if(!vec.IsNull() && val > GFX_ZERO_TOLERANCE)
    {
    gfxDimensionalUnit<N> res(vec);
    return res /= val;
    }
    else
    return gfxDimensionalUnit<N>();
    }

    // returns the vector scaled by a value val
    friend gfxDimensionalUnit<N> operator *(const gfxDimensionalUnit<N>& vec, double val)
    {
    if(!vec.IsNull())
    {
    gfxDimensionalUnit<N> res(vec);
    return res *= val;
    }
    else
    return gfxDimensionalUnit<N>();
    }

    // returns the vector scaled by a value val
    friend gfxDimensionalUnit<N> operator *(double val, const gfxDimensionalUnit<N>& vec)
    {
    return vec * val;
    }

    // writes the N components in the ostream
    friend std::ostream & operator <<(std::ostream& os, const gfxDimensionalUnit<N>& vec)
    {
    char buffer[80];
    for(int i = 0; i < N; i++)
    {
    sprintf_s(buffer,"%.3f ",vecIdea);
    os << buffer;
    }
    os << std::endl;
    return os;
    }

    // reads the N elements from the istream
    friend std::istream& operator >>(std::istream& is, gfxDimensionalUnit<N>& vec)
    {
    for(int i = 0; i < N; i++)
    is >> vecIdea;
    vec.m_Initialized = true;
    return is;
    }

    virtual gfxType GetType() const
    {
    return GFX_DIMENSIONAL_UNIT;
    }

    protected:
    // Flag that is true when the vector has been initialized.
    bool m_Initialized;

    // The components of the vector.
    double m_dataNo;
    };

    #endif

    He is the class that inherits from it

    The header


    #ifndef gfxVector2_h
    #define gfxVector2_h 1

    #include "gfxDimensionalUnit.h"

    class vnl_double_2;
    class gfxPoint2;

    class gfxVector2 : public gfxDimensionalUnit<2>

    {
    public:
    gfxVector2();

    gfxVector2(const gfxVector2 &right);

    // Constructor from a vector of double of 2 elements
    gfxVector2(double *vec);

    // Constructor from a 2D DimensionalUnit
    gfxVector2(const gfxDimensionalUnit<2>& vec);

    // Contructor from two values
    gfxVector2(double x, double y);

    // Constructor from two 2D points, it makes the vector that
    // goes from A to B
    //gfxVector2(const gfxPoint2& A, const gfxPoint2& B);

    // Constructor from a vnl vector
    gfxVector2(const vnl_double_2& vec);

    // dor multiplication of two vectors
    double operator *(const gfxVector2& vec) const;

    // returns the norm of the vector
    double GetLength() const;

    // returns the squared norm of the vector
    double GetSquaredLength() const;

    // Normalize the vector
    const gfxVector2& Normalize();

    // return a normalized vector with the same direction and
    // sense of this one
    gfxVector2 as_unit() const;

    // Inverts the vector
    gfxVector2 operator -() const;

    vnl_double_2 as_vnl() const;

    virtual gfxType GetType() const;
    };



    // Class gfxVector2

    inline gfxVector2::gfxVector2(double *vec)
    : gfxDimensionalUnit<2>(vec,2)
    {
    }

    inline gfxVector2::gfxVector2(const gfxDimensionalUnit<2>& vec)
    : gfxDimensionalUnit<2>(vec)
    {
    }

    inline gfxVector2::gfxVector2(double x, double y)
    {
    m_data[0] = x;
    m_data[1] = y;
    m_Initialized = true;
    }

    inline gfxVector2 gfxVector2::operator -() const
    {
    return gfxVector2(-m_data[0],-m_data[1]);
    }

    inline gfxType gfxVector2::GetType() const
    {
    return GFX_VECTOR2;
    }
    #endif


    And the cpp

    #include "gfxVector2.h"
    #include "vnl\vnl_double_2.h"

    gfxVector2::gfxVector2()
    {
    }

    gfxVector2::gfxVector2(const gfxVector2 &right)
    :gfxDimensionalUnit<2>(right)
    {
    }

    gfxVector2::gfxVector2(const vnl_double_2& vec)
    {
    m_data[0] = vec(0);
    m_data[1] = vec(1);
    m_Initialized = true; //vnl vectors can't be null.
    }
    double gfxVector2::operator *(const gfxVector2& vec) const
    {
    return m_data[0] * vec[0] + m_data[1] * vec[1];
    }

    double gfxVector2::GetLength() const
    {
    return sqrt(m_data[0]*m_data[0] + m_data[1]*m_data[1]);
    }

    double gfxVector2::GetSquaredLength() const
    {
    return m_data[0]*m_data[0] + m_data[1]*m_data[1];
    }

    const gfxVector2& gfxVector2::Normalize()
    {
    if(IsNull())
    return *this;

    double dLength = GetLength();

    if ( dLength > GFX_ZERO_TOLERANCE )
    {
    double dInvLength = 1.0/dLength;
    m_data[0] *= dInvLength;
    m_data[1] *= dInvLength;
    }
    else
    {
    //Defensive assignment.
    m_data[0] = 0.0;
    m_data[1] = 0.0;
    m_Initialized = false;
    }

    return *this;
    }

    gfxVector2 gfxVector2::as_unit() const
    {
    gfxVector2 vec(*this);
    return vec.Normalize();
    }

    vnl_double_2 gfxVector2::as_vnl() const
    {
    return vnl_vector_fixed<double,2>(m_data);
    }

    gfxVector2 NullVector2;


  • mstrecman

    That probably means that you have exported the template class from that dll and you have implemented some functions of the template class in a .cpp file instead of the header file of that template. Some more details like how do you declare that template class will help give a more definitive answer.
  • Tim Mercer

    (The strange thing is that the undecorated name of the reported " _7 $gfxDimensionalUnit@$02@@6B@" string actually seems to be "const gfxDimensionalUnit<3>::`vftable'").


  • tsg

    You have to put a template class solely in the header. The template is instantiated only when you use it in the code. That means your library could compile fine (because the template class is not yet generated and compiled), but the client code will break.

  • arse

    I'm feeling fairly certain that the problem is in your project or header setup, though. I downloaded the VNL library, compensated the other missing types and compiled a static library from the code you provided. That worked out just fine. Even with use in an external project, everything compiled like a charm.

    Could you shed any more light on your setup, or perhaps make your project publicly available



  • mt2

    Do you have a pragma once or ifndef+define combination in the top of the header which defines gfxDimensionalUnit

  • Rivan56

    The error comes when another lib or program use this lib, because template symbols are included on the lib althought the template is not exported and all template code is declared on the header.
    The specific error that happens when linking another library that uses the given is:

    GfxBase.lib(GfxBase.dll) : error LNK2005: "public: __thiscall gfxDimensionalUnit<2>::GetComponents(void)" ( _7 $gfxDimensionalUnit@$02@@6B@) already defined in gfxArcFinder.obj

  • Aaron Oneal

    Please include error information when you paste such extensive blocks of code. That saves us all some time. Thanks!

  • alphonso

    Yes I have
    #ifndef gfxDimensionalUnit_h
    #define gfxDimensionalUnit_h 1


  • Tryin2Bgood

    Sorry for the delay guys, I had an job interrupt I couldn't avoid. I tried to make a simple version of my library that contains the problem, but is not that easy. I will post it as soon as I have it.



  • linking problems with templates