Link to home
Start Free TrialLog in
Avatar of galdek
galdek

asked on

templates : Linker Unresolved External

given a template class:
template<class T> class G4WPtr {
  public:
      //some inline methods


};
//some NON inline methods

when I use it it works fine BUT..

in foo.h (in a DLL)
class A {
....
G4WPtr<long> p_long;

}

in ggg.cpp (in another EXE/DLL)
#include "foo.h"

Linker : Unresolved External on G4WPtr<long>::DefRef()
(which is a non inline method)

1) what's the problem here ?
2) what is this ?  
template class _export G4WPtr<MyClass>;
(in a header file )
ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nietod
nietod

You probably have a header (.h) file that includes the template class definition into the other translation unit (EXE/DLL), but this header does not include the defintion of the member function, the actual code of the function, it just has the declaration (return value and parameters).

What you need to do is move the member function definitions from the DLL/EXE that now contains them to the header file (.h) that contains the template definiton.

Let me know, if you have any questions.
What do you mean in qustion 2?
Avatar of galdek

ASKER

I need an example:
this template class is indeed in a header file g4wptr.h
it is a smart pointer to be used in many cpp files in different
exe/dlls.
I want to #include g4wptr.h in each such cpp/h.
I cant add to g4wptr.h each new instantiation of this template,
ex: G4WPtr<MyClass> dummy;
I know that if I put in the exe/dll such a dummy it will link but
I'm trying to avoid that (filling my code with dummy dec.)
please send a code example of a typical solution to such a case
It would be best if you posted your code so I could shouw how to change that, but

You probably have

//grwptr.h
template<class T> class G4WPtr {
     public:
         void DefRef();  // Declaration, not definition.
}

// In some .cpp.  Maybe grwotr.cpp?
template<class T>
void Q4WPtr<t>::DefRef()
{

};  // Definition.

The problem is that this definition must be visible to other translation units.  (.cpp files).  Thus this defintion must be moved into the . h file to get.


// New grwptr.h
template<class T> class G4WPtr {
     public:
         void DefRef();  // Declaration, not definition.
}

template<class T>
void Q4WPtr<t>::DefRef()
{

};  // Definition in .h file.

If that doesn't help, post your code if possible.  If not, you can e-mail it to me at nietod@theshop.net
Avatar of galdek

ASKER

file g4wptrEx.h
#if !defined (_G4W_PTREX_TEST_2)
#define  _G4W_PTREX_TEST_2
//#include "g4whead.h"
#include "windows.h"

#ifdef G4WDEBUG
#define PtrAssert( cond , errorTypeEnum) \
( cond ) ? (void)0 :  Error(errorTypeEnum)
#else
#define PtrAssert( cond , errorTypeEnum)
#endif

//-----------------------------------------------------------------------------
//  RefRep class:
//  -------------
//  contains void pointer to object and reference counter.
//  for each object pointed by smart ptr ONE RefRep is created on the heap
//  and it's RefCount is set to 1. when another smart pointer also point
//  at the object the RefCount is inc by 1. when a smart pointer stops
//  pointing at the object RefCount is dec by 1.
//
//
//       Creation:  Dekel Cohen 04/98
//-----------------------------------------------------------------------------
class /*_G4WCORECLASS*/ RefRep {
      public:
      enum ErrorTypes { MoreDeleteThanNew};
      explicit RefRep(void* object) : Object(object), RefCount(1)
      {
         //#ifdef G4WDEBUG
            //lettersCounter++;
         //LOGSTR("RefRep::RefRep after ++ lettersCounter =" << lettersCounter);
         //#endif
      }
     ~RefRep() {}

      void AddRef()
      {
         //#ifdef G4WDEBUG
         //lettersCounter++;
         //LOGSTR("RefRep::AddRef() after ++ lettersCounter =" << lettersCounter);
         //#endif
            RefCount++;
      }
      //#ifdef G4WDEBUG
      //int Release();
      //#else
      int Release()
      {
            RefCount--;
         return (RefCount);
      }
      //#endif
      void*  Object;
      int RefCount;
      static RefRep NullLetter;
      private:
      #ifdef G4WDEBUG
      static int lettersCounter;
      #endif
      #ifdef G4WDEBUG
          void Error(ErrorTypes errorType);
          #endif


};


//-----------------------------------------------------------------------------
//  Smart Pointer class.
//  Replaces c pointers (dumb pointers) in most of programming tasks
//  Smart Pointer OWNS the c - pointer passed to it (by constructor or operator =)
//       when dealing with smart ptrs you shouldn't use delete EVER.
//  the smart ptr will decrease ref count on this pointer until it reaches 0
//  than - when no smart ptr is pointing at it, it will be deleted.
//
//  Decleration Examples:
//  **************************
//  local scope
//  ------------
//  G4WPtrEx<G4WAgent> agentPtr( new G4WAgent(...constructor params..) );
//  ** don't use G4WPtrEx<G4WAgent> agentPtr = new G4WAgent(...constructor params..);
//     cause it will not compile
//  class member
//  ------------
//  protected:
//            G4WPtrEx<G4WAgent> agentPtr
//
//  parameter to function
//      -----------------------
//  void Func(G4WPtrEx<G4WAgent>& agentPtr)
//
//  array of pointers
//  ------------------
//  typedef TArrayAsVector< G4WPtrEx<BasePass1> > Pass1Array;
//
//       examples of uses
// -------------------
//  1) agentPtr->Skip();
//       3) *smartptr_to_int=13;
//  4) OldFunc( smartPtr.c_ptr() ) - backward competability with functions
//                                                                         recieving regular c pointers only
//  Don'ts
//  --------
//  1) never pass the same c - pointer to 2 different smart ptr.
//          G4WPtrEx<Shape> sp1(p); // Okay
//     G4WPtrEx<Shape> sp2(p); // Not!!!!
//  2) never pass the address of stack objects to smart ptrs. only heap objects
//             allowed.
//     G4WCode6Obj localObject;
//     G4WPtrEx<G4WCode6Obj> badPtr( &localObject );
//  3) Circular Refference Problem
//             ---------------------------
//     don't put a smartptr in object A pointing at B. another ptr in
//             B pointing at C and in C pointing at A.
//             it will cause endless loop.
//     this situation is pretty rare so just pay attention.
//  4) never pass a c - pointer to smart ptr and delete it yourself (no one
//          but smartPtr should delete it, inc OWL (in dialogs) and Win API.)
//     p=new Shape;
//     G4WPtrEx<Shape> sp1(p); // Okay
//     delete p; // Not!!!!
//     * if smart pointer delete it first and "delete p;" comes after it's more
//               difficult to debug.
//     important:
//     -------------------
//     the exception handling does not always catch the double delete
//     problem, so G4WPtrEx will not report always in a nice messagebox
//     what happned. also form inside borland debugger, the debugger
//     catch the exception first so it will look like normal GPF.
//     from out side the debugger there is a good chance that the explaining
//     message will appear.
//       5) G4WPtrEx is not a ptr to c-Array!!!
//             ----------------------------------
//     Shape* p=new Shape[30];
//             G4WPtrEx<Shape> sp1(p); // Not!!!!
//             it will delete p with "delete p" and not "delete [] p" as it should
//       Creation:  Dekel Cohen 1996-1998
//-----------------------------------------------------------------------------
template<class T> class G4WPtrEx {
  public:

        enum ErrorTypes { LetterNonInit,PointerNonInit,LetterOrPointerNonInit,DeleteFailed,
                                       TwoOwnersSameDumbPtr,InvalidBaseToDerivAssign,AccessViolation };
  private:


  public:
    G4WPtrEx() : Letter(&RefRep::NullLetter)
    {
            #ifdef G4WDEBUG
      ptr=NULL;
      #endif
    }
    explicit G4WPtrEx(T* object) : Letter(new RefRep(object))
    {
            #ifdef G4WDEBUG
      PtrAssert(!object || !IsBadWritePtr(object,1),AccessViolation);
      ptr=(T*)Letter->Object;
      #endif
    }
    G4WPtrEx(const G4WPtrEx& src) : Letter(src.Letter)
    {
      Letter->AddRef();
      #ifdef G4WDEBUG
      ptr=(T*)Letter->Object;
      #endif
    }
   ~G4WPtrEx() { DecRef(); }

    G4WPtrEx& operator =(const G4WPtrEx& src);
    G4WPtrEx& operator =(T* object);
    //compare c-ptrs between 2 smart ptrs
    int operator==(const G4WPtrEx& theRight) const
    {
      return( Letter->Object == theRight.Letter->Object );
    }
    //compare c-ptrs between a smart ptrs and c_ptr
    int operator==(const T* object) const
    {
      return( (T*)Letter->Object == object );
    }

    //true if underlying c-pointer is not null.
    bool IsDumbPtrNull()
    {
          return (Letter->Object==NULL);
    }
    //makes smartptr look like c-ptr: agentPtr->GetId()
    //checks if it's a valid ptr
    T* operator->()
    {
      PtrAssert(Letter->Object,PointerNonInit);
          return (T*)Letter->Object;
    }
    //dereference smartptr the same way c-ptr works
    T& operator *()
    {
          PtrAssert(Letter->Object,PointerNonInit);
          return *( (T*)Letter->Object );
    }

    bool operator!()
    {
            return (Letter->Object==NULL);
    }
       T* Unsafe_c_ptr()
    {
          return (T*)Letter->Object;
    }
    //return c-ptr (dumbptr) - dangerous!!!! used to pass
    //smart ptr to functions recieve c-ptr.
    T* c_ptr()
    {
      PtrAssert(!Letter->Object || !IsBadWritePtr(Letter->Object,1),AccessViolation);
          return (T*)Letter->Object;
    }
    void MakeNull()
    {
            DecRef();
           Letter = &RefRep::NullLetter;
      #ifdef G4WDEBUG
      ptr=NULL;
      #endif
    }
    //Polymorphic emulation related methods
    void IsBaseClassOf(T* derivedDumbPtr) { };
    //Don't Call this method directly !!! used by polymorphic assignment funcs
    void AssignLetter(RefRep *newLetter);
    //Don't Call this method directly !!! used by polymorphic assignment funcs
    RefRep* AsLetter() { return Letter; }
    //Don't Call this method directly !!! used by polymorphic assignment funcs
    #ifdef G4WDEBUG
    void Error(ErrorTypes errorType);
    #endif

  private:
    //report error of using non-init ptr


       void DecRef();

    RefRep* Letter;

    #ifdef G4WDEBUG
    T*  ptr;
    #endif
};



#ifdef G4WDEBUG
template<class T>
void G4WPtrEx<T>::DecRef()
{
  if (Letter == &RefRep::NullLetter) { return; }
  try {
         if (Letter->Release()==0 )
         {
            //LOGSTR("Type of deleted : " << typeid(T).name() );
            delete (T*)Letter->Object;
            delete Letter;
            Letter = &RefRep::NullLetter;
            ptr=NULL;
         }
     } except (EXCEPTION_EXECUTE_HANDLER)
     {
         Error(DeleteFailed);
     }

}
#else //Non Debug Destructor
template<class T>
void G4WPtrEx<T>::DecRef()
{
  if (Letter == &RefRep::NullLetter) { return; }
  if (Letter->Release()==0 )
  {
     delete (T*)Letter->Object;
     delete Letter;
     Letter = &RefRep::NullLetter;
  }
}
#endif
//-----------------------------------------------------------------------------
//  Purpose      : messagebox to smartptr user informing of using non-init ptr
//            user response : Yes - causes GPF (good to inspect call stack)
//                                                            No- terminate process now.
//                                                            Cancel - ignore and continue execution
//  Receive      :const char *fileName - __FILE__ cpp file name which the error occur
//                         int line - __LINE__ - no. of line in cpp which produced error
//  Return      :  --
//       Creation:  Dekel Cohen 04/98
//-----------------------------------------------------------------------------
#ifdef G4WDEBUG
template<class T>
void G4WPtrEx<T>::Error(ErrorTypes errorType)
{
   char message[300];
   if (errorType==LetterOrPointerNonInit)
   {
         if (!Letter)
      {
        errorType=LetterNonInit;
      } else if (!Letter->Object)
      {
                  errorType=PointerNonInit;
      }
   }
   if (errorType==AccessViolation && !Letter->Object)
   {
      errorType=PointerNonInit;
   }
   switch( errorType )
   {

      case LetterNonInit:
            sprintf(message,"non Init G4WPtrEx used\nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES"  );
            break;
      case PointerNonInit:
            sprintf(message,"NULL G4WPtrEx used with ptr->method/var:\nYes - access violation for call stack\nNo - Abnormal termination\n break point at g4wptrex.h near case IDYES");
            break;
      case DeleteFailed:
            sprintf(message,"GPF when deleting\n1) same ptr Deleted Twice !!!\n someone passed c-ptr to smartPtr and deleted the c pointer himself.\n2) smart-ptr points to stack object!!\n3)two G4W pointers were assigned the same c-ptr\nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES");
            break;
      case TwoOwnersSameDumbPtr:
         sprintf(message,"1)two different letters were assigned the same c-ptr\nThat means it will be deleted twice!!!\nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES");
            break;
             case InvalidBaseToDerivAssign:
         sprintf(message,"1)Invalid cast of base to derived type\nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES");
            break;
      case AccessViolation:
         sprintf(message,"1)pointer points to memory with no write premisions\nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES");
            break;



          default: sprintf(message,"Unknown SmartPtr Error: \nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES");
   }
   int choice=::MessageBox( 0, message, "G4WPtrEx Assertion", MB_YESNOCANCEL | MB_ICONQUESTION );
   switch(choice)
   {
          case IDYES:
      {
            char *p=NULL;
            *p=12;
         break;
      }
      case IDNO:
         abort();
         break;

      case IDCANCEL:
         break;

            default: ::MessageBox( 0, "Only IDYES,IDNO,IDCANCEL ", "Invalid switch option",MB_OK);
   } //End Switch

}
#endif
//-----------------------------------------------------------------------------
//  Purpose      : assignment of G4WPtrEx to another G4WPtrEx.
//                          inc ref count of letter of theRight and release it's old Letter
//  Receive      : theRight - G4WPtrEx to assign from
//  Return      : smart ptr of type T
//       Creation: Dekel Cohen 04/98
//-----------------------------------------------------------------------------
template<class T>
G4WPtrEx<T>& G4WPtrEx<T>::operator =(const G4WPtrEx<T>& src)
{
  if (src.Letter != Letter )
  {
     #ifdef G4WDEBUG
     if (src.Letter->Object==Letter->Object && Letter->Object!=NULL)
     {
        Error(TwoOwnersSameDumbPtr);
     }
     #endif
     DecRef(); //@D optimize : double Letter=NullLetter; Letter=src.Letter;
     Letter = src.Letter;
     Letter->AddRef();
     #ifdef G4WDEBUG
     ptr=(T*)Letter->Object;
     #endif
  }
  return *this;
}
//-----------------------------------------------------------------------------
//  Purpose      : assignment of G4WPtrEx to another G4WPtrEx which is a base class ptr.
//                          inc ref count of letter of theRight and release it's old Letter
//  Receive      : theRight - G4WPtrEx to assign from
//  Return      : smart ptr of type T
//       Creation: Dekel Cohen 04/98
//-----------------------------------------------------------------------------
template<class T>
void G4WPtrEx<T>::AssignLetter(RefRep *newLetter)
{
  if (newLetter!=Letter)
  {
     #ifdef G4WDEBUG
     if (newLetter->Object==Letter->Object && Letter->Object!=NULL)
     {
        Error(TwoOwnersSameDumbPtr);
     }
     PtrAssert(!newLetter->Object || !IsBadWritePtr(newLetter->Object,1),AccessViolation);
     #endif

     DecRef(); //@D optimize : double Letter=NullLetter; Letter=src.Letter;
     Letter=newLetter;
     #ifdef G4WDEBUG
     ptr=(T*)Letter->Object;
     #endif
     Letter->AddRef();
  }
}
//-----------------------------------------------------------------------------
//  Purpose      : assignment of c (dumb) - pointer to G4WPtrEx.
//                          release it's old Letter and makes a new one form c - pointer.
//  Receive      : theRight - G4WPtrEx to assign from
//  Return      : smart ptr of type T
//       Creation: Dekel Cohen 04/98
//-----------------------------------------------------------------------------
template<class T>
G4WPtrEx<T>& G4WPtrEx<T>::operator =(T* object)
{

  if ( object!=Letter->Object )
  {
    PtrAssert(!object || !IsBadWritePtr(object,1),AccessViolation);
    DecRef(); //@D optimize: double if ( Letter ) and double Letter=
    Letter = new RefRep(object);  // Assumes non-null! Use with new
  }
  #ifdef G4WDEBUG
  ptr=(T*)Letter->Object;
  #endif
  return *this;
}

//Polymorphic assignment
template<class B,class D>
void DerivToBaseEx(G4WPtrEx<D>& deriv,G4WPtrEx<B>& base)
{
      base.IsBaseClassOf(deriv.Unsafe_c_ptr());
   base.AssignLetter(deriv.AsLetter());
}

template<class B,class D>
void BaseToDerivEx(G4WPtrEx<B>& base,G4WPtrEx<D>& deriv)
{
   if ( !TYPESAFE_DOWNCAST(base.Unsafe_c_ptr(),D) )
   {
         deriv.AssignLetter(&RefRep::NullLetter);
   }
   deriv.AssignLetter(base.AsLetter());
}
#endif //of include

file g4wptrEx.cpp
#include "windows.h"
#include "stdio.h"
#pragma hdrstop


//------------------------------
#include "smartPtr\G4wPtrEx.h"
//------------------------------
#ifdef G4WDEBUG
int RefRep::lettersCounter=0;
#endif

RefRep RefRep::NullLetter(NULL);




#ifdef G4WDEBUG
void RefRep::Error(ErrorTypes errorType)
{
   char message[300];

   switch( errorType )
   {
          case MoreDeleteThanNew:
            sprintf(message,"More pointers deleted than newed - Check Circular Reference:nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES" );
            break;

          default: sprintf(message,"Unknown SmartPtr Letter Error\nYes - access violation for call stack\nOr put break point at g4wptr.h near case IDYES" );
   }
   int choice=::MessageBox( 0, message, "G4WPtr Letter Assertion", MB_YESNOCANCEL | MB_ICONQUESTION );
   switch(choice)
   {
          case IDYES:
      {
            char *p=NULL;
            *p=12;
         break;
      }
      case IDNO:
         abort();
         break;

      case IDCANCEL:
         break;

            default: ::MessageBox( 0, "Only IDYES,IDNO,IDCANCEL ", "Invalid switch option",MB_OK);
   } //End Switch

}
#endif

That code compiled fine for me.  Are you having a problem with it?  (I added another source code file that has a main() and and compiled  andl linked the two .cpp files (yours and my main) with no problems.
If you are having problems, is it the same one?  what compiler are you using?
Avatar of galdek

ASKER

I use BC++ v5.02.
the problem is only if you have at least one dll and another
dll/exe. in dll define a class with a smart pointer member
and in exe include this class and use it (with lib of the dll, like any other export). should get unresolved external.
The problem is (in part at least) that the template class GW4PtrEx is not delcated _declspec(dllexport)  In addition, the RefRep class will need to be as well.  If that doesn't help, can you send me the source code for the DLL, EXE and the header files?  

My e-mail address is nietod@theshop.net