2009-04-16 11 views
5

Sto lavorando su un progetto con una DLL e un file EXE in Visual Studio 2005. Tra il codice per la DLL è un modello per una classe Array growable:C++ Moltiplicare simboli definiti con un colpo di testa definita classe template

template <class Type> 
class GArray 
{ 
    Type *p; 
    uint32 len; 
    uint32 alloc; 

protected: 
    bool fixed; 

public: 
    /// Constructor 
    GArray(int PreAlloc = 0) 
    { 
     p = 0; 
     len = 0; 
     fixed = false; 

     alloc = PreAlloc; 
     if (alloc) 
     { 
      int Bytes = sizeof(Type) * alloc; 
      p = (Type*) malloc(Bytes); 
      if (p) 
      { 
       memset(p, 0, Bytes); 
      } 
      else 
      { 
       alloc = 0; 
      } 
     } 
    } 

    /// Destructor 
    ~GArray() 
    { 
     Length(0); 
    } 

    /// Returns the number of used entries 
    uint32 Length() const 
    { 
     return len; 
    } 

    /// Sets the length of available entries 
    bool Length(uint32 i) 
    { 
     if (i > 0) 
     { 
      if (i > len && fixed) 
       return false; 

      uint nalloc = alloc; 
      if (i < len) 
      { 
       // Shrinking 
      } 
      else 
      { 
       // Expanding 
       int b; 
       for (b = 4; (1 << b) < i; b++) 
        ; 
       nalloc = 1 << b; 
       LgiAssert(nalloc >= i); 
      } 

      if (nalloc != alloc) 
      { 
       Type *np = (Type*)malloc(sizeof(Type) * nalloc); 
       if (!np) 
       { 
        return false; 
       } 

       if (p) 
       { 
        // copy across common elements 
        memcpy(np, p, min(len, i) * sizeof(Type)); 
        free(p); 
       } 
       p = np; 
       alloc = nalloc; 
      } 

      if (i > len) 
      { 
       // zero new elements 
       memset(p + len, 0, sizeof(Type) * (i - len)); 
      } 

      len = i; 
     } 
     else 
     { 
      if (p) 
      { 
       int Length = len; 
       for (uint i=0; i<Length; i++) 
       { 
        p[i].~Type(); 
       } 
       free(p); 
       p = 0; 
      } 
      len = alloc = 0; 
     } 

     return true; 
    } 

    GArray<Type> &operator =(const GArray<Type> &a) 
    { 
     Length(a.Length()); 
     if (p && a.p) 
     { 
      for (int i=0; i<len; i++) 
      { 
       p[i] = a.p[i]; 
      } 
     } 
     return *this; 
    } 

    /// \brief Returns a reference a given entry. 
    /// 
    /// If the entry is off the end of the array and "fixed" is false, 
    /// it will grow to make it valid. 
    Type &operator [](uint32 i) 
    { 
     static Type t; 
     if 
     (
      i < 0 
      || 
      (fixed && i >= len) 
     ) 
     { 
      ZeroObj(t); 
      return t; 
     } 

     #if 0 
     if (i > 15000000) 
     { 
      #if defined(_DEBUG) && defined(_MSC_VER) 
      LgiAssert(0); 
      #endif 

      ZeroObj(t); 
      return t; 
     } 
     #endif 

     if (i >= alloc) 
     { 
      // increase array length 
      uint nalloc = max(alloc, GARRAY_MIN_SIZE); 
      while (nalloc <= i) 
      { 
       nalloc <<= 1; 
      } 

      // alloc new array 
      Type *np = (Type*) malloc(sizeof(Type) * nalloc); 
      if (np) 
      { 
       // clear new cells 
       memset(np + len, 0, (nalloc - len) * sizeof(Type)); 
       if (p) 
       { 
        // copy across old cells 
        memcpy(np, p, len * sizeof(Type)); 

        // clear old array 
        free(p); 
       } 

       // new values 
       p = np; 
       alloc = nalloc; 
      } 
      else 
      { 
       static Type *t = 0; 
       return *t; 
      } 
     } 

     // adjust length of the the array 
     if (i + 1 > len) 
     { 
      len = i + 1; 
     } 

     return p[i]; 
    } 

    /// Delete all the entries as if they are pointers to objects 
    void DeleteObjects() 
    { 
     for (uint i=0; i<len; i++) 
     { 
      DeleteObj(p[i]); 
     } 
     Length(0); 
    } 

    /// Delete all the entries as if they are pointers to arrays 
    void DeleteArrays() 
    { 
     for (int i=0; i<len; i++) 
     { 
      DeleteArray(p[i]); 
     } 
     Length(0); 
    } 

    /// Find the index of entry 'n' 
    int IndexOf(Type n) 
    { 
     for (uint i=0; i<len; i++) 
     { 
      if (p[i] == n) return i; 
     } 

     return -1; 
    } 

    /// Returns true if the item 'n' is in the array 
    bool HasItem(Type n) 
    { 
     return IndexOf(n) >= 0; 
    } 

    /// Deletes an entry 
    bool DeleteAt 
    (
     /// The index of the entry to delete 
     uint Index, 
     /// true if the order of the array matters, otherwise false. 
     bool Ordered = false 
    ) 
    { 
     if (p && Index >= 0 && Index < len) 
     { 
      // Delete the object 
      p[Index].~Type(); 

      // Move the memory up 
      if (Index < len - 1) 
      { 
       if (Ordered) 
       { 
        memmove(p + Index, p + Index + 1, (len - Index - 1) * sizeof(Type)); 
       } 
       else 
       { 
        p[Index] = p[len-1]; 
       } 
      } 

      // Adjust length 
      len--; 
      return true; 
     } 

     return false; 
    } 

    /// Deletes the entry 'n' 
    bool Delete 
    (
     /// The value of the entry to delete 
     Type n, 
     /// true if the order of the array matters, otherwise false. 
     bool Ordered = false 
    ) 
    { 
     int i = IndexOf(n); 
     if (p && i >= 0) 
     { 
      return DeleteAt(i, Ordered); 
     } 

     return false; 
    } 

    /// Appends an element 
    void Add 
    (
     /// Item to insert 
     const Type &n 
    ) 
    { 
     (*this)[len] = n; 
    } 

    /// Appends multiple elements 
    void Add 
    (
     /// Items to insert 
     Type *s, 
     /// Length of array 
     int count 

    ) 
    { 
     if (!s || count < 1) 
      return; 

     int i = len; 
     Length(len + count); 
     Type *d = p + i; 
     while (count--) 
     { 
      *d++ = *s++; 
     } 
    } 

    /// Inserts an element into the array 
    bool AddAt 
    (
     /// Item to insert before 
     int Index, 
     /// Item to insert 
     Type n 
    ) 
    { 
     // Make room 
     if (Length(len + 1)) 
     { 
      if (Index < len - 1) 
      { 
       // Shift elements after insert point up one 
       memmove(p + Index + 1, p + Index, (len - Index - 1) * sizeof(Type)); 
      } 
      else if (Index >= len) 
      { 
       // Add at the end, not after the end... 
       Index = len - 1; 
      } 

      // Insert item 
      p[Index] = n; 

      return true; 
     } 

     return false; 
    } 

    /// Sorts the array 
    void Sort(int (*Compare)(Type*, Type*)) 
    { 
     typedef int (*qsort_compare)(const void *, const void *); 
     qsort(p, len, sizeof(Type), (qsort_compare)Compare); 
    } 

    /// \returns a reference to a new object on the end of the array 
    Type &New() 
    { 
     return (*this)[len]; 
    } 

    /// Returns the memory held by the array and sets itself to empty 
    Type *Release() 
    { 
     Type *Ptr = p; 
     p = 0; 
     len = alloc = 0; 
     return Ptr; 
    } 
}; 

Ho riutilizzato questo codice nel file EXE in diversi punti. Tuttavia quando lo uso in un unico file particolare mi iniziare a ricevere errori di collegamento simbolo duplicato:

2>lgi8d.lib(Lgi8d.dll) : error LNK2005: "public: int __thiscall GArray<char *>::Length(void)" ([email protected][email protected]@@QAEHXZ) already defined in FrameStore.obj 
2>D:\Home\matthew\network_camera\src\vod_test\Debug\vod_test.exe : fatal error LNK1169: one or more multiply defined symbols found 

ho usato la stessa classe in altri file nel file EXE senza errori. per esempio. in Camera.cpp ho:

void DeleteFromArray(GArray<char> &a, int Start, int Len) 
{ 
    assert(Len >= 0); 

    int64 StartTs = LgiCurrentTime(); 

    int Del = min(Len, a.Length() - Start); 
    if (Del > 0) 
    { 
     int Remain = a.Length() - Start - Del; 
     if (Remain > 0) 
     { 
      memmove(&a[Start], &a[Start+Del], Remain); 
      MoveBytes += Remain; 
      a.Length(Start+Remain); 
     } 
     else a.Length(Start); 
    } 

    int64 End = LgiCurrentTime(); 
    DeleteTime += End - StartTs; 
} 

che riunisce e collega ok ... ma in FrameStore.cpp:

void Scan() 
{ 
    if (!Init) 
    { 
     Init = true; 
     GAutoString Path = FrameFile::GetPath(); 
     GAutoPtr<GDirectory> Dir(FileDev->GetDir()); 
     GArray<char*> k; 

     int64 Size = 0; 
     for (bool b = Dir->First(Path); b; b = Dir->Next()) 
     { 
      if (!Dir->IsDir()) 
      { 
       char *e = LgiGetExtension(Dir->GetName()); 
       if (e && !stricmp(e, "mjv")) 
       { 
        char p[MAX_PATH]; 
        Dir->Path(p, sizeof(p)); 
        k.Add(NewStr(p)); 
        Size += Dir->GetSize(); 
       } 
      } 
     } 

     GAutoPtr<Prog> p(new Prog(Size)); 
     for (int i=0; i<k.Length(); i++) 
     { 
      Files.Add(new FrameFile(k[i], p)); 
     } 

     k.DeleteArrays(); 
    } 
} 

causa l'errore di collegamento sulla linea con "k.Length()" in esso ... se commento che questo link! Tuttavia sto usando altri metodi nella classe GArray nello stesso codice e non causano alcun problema.

Perché un modello di classe definito interamente in un'intestazione deve avere questo problema in primo luogo?

+0

Stavo per dire condizionatamente includere il file utilizzando #ifndef FOO, #define FOO, CODE, #endif. Ma penso che il problema è che l'istanza del modello che stai istanziando viene definita due volte. – ojblass

+0

Probabilmente non correlato al tuo errore di collegamento ma la len del membro è dichiarata come uint32 ma tu stai tornando e int da Length(). Anche quella versione di Length potrebbe/dovrebbe essere dichiarata const, non stai modificando nulla, ad es. Uint Length() const {return len; } – maccullt

+0

niente di semplice qui: :( puoi provarlo su linux? – ojblass

risposta

3

Il problema:

C'è un'altra classe definita in Lgi.dll che esporta l'istanza Garray.

#ifdef LGI_DLL 
    #define LgiClass  __declspec(dllexport) 
#else 
    #define LgiClass  __declspec(dllimport) 
#endif 

class LgiClass GToken : public GArray<char*> 
{ 
    /// stuff... 
}; 

La soluzione:

includono tale GToken intestazione nel mio EXE, in particolare il file FrameStore.cpp che utilizza l'implementazione Garray, allora la compilazione importerà quei simboli dalla DLL invece di duplicare.

Sarebbe stato bello se il compilatore potesse darmi un indizio su dove la DLL stava definendo il simbolo. Dire semplicemente "c'è un duplicato da qualche parte" non è molto utile.

0

Se si sta utilizzando Visual Studio 6, accertarsi che la seguente opzione è impostata:

Progetto-> Impostazioni-> C/C++ -> Codice generazione-> Usa libreria di runtime ===> Debug Multithreaded/Multithreaded

MODIFICA: In VS 2005 è praticamente lo stesso.

Progetto-> Proprietà-> Configurazione Proprietà-> C/C++ -> Codice generazione-> Durata Library-> multi-threaded/multi-threaded Debug

+0

Io uso [debug/release] multi-thread DLL per la libreria di runtime sia in vc6 che in vs200x – fret

0

Come nota a margine, si potrebbe voler dividere le dichiarazioni e le definizioni, quindi non è abbastanza brutto:

template <class Type> 
class GArray 
{ 
    Type *p; 
    uint32 len; 
    uint32 alloc; 

protected: 
    bool fixed; 

public: 
    GArray(int PreAlloc = 0); 

    /// Destructor 
    ~GArray() {Length(0);}  

    /// Returns the number of used entries 
    int Length() {return len;} 

    /// Sets the length of available entries 
    bool Length(uint32 i); 

    // etc... 
}; 


template <class Type> 
GArray<Type>::GArray(int PreAlloc = 0) 
{ 
    p = 0; 
    len = 0; 
    fixed = false; 

    alloc = PreAlloc; 
    if (alloc) 
    { 
     int Bytes = sizeof(Type) * alloc; 
     p = (Type*) malloc(Bytes); 
     if (p) 
     { 
      memset(p, 0, Bytes); 
     } 
     else 
     { 
      alloc = 0; 
     } 
    } 
} 

template <class Type> 
bool GArray<Type>::Length(uint32 i); 
{ 
    if (i > 0) 
    { 
     if (i > len && fixed) 
      return false; 

     uint nalloc = alloc; 
     if (i < len) 
     { 
      // Shrinking 
     } 
     else 
     { 
      // Expanding 
      int b; 
      for (b = 4; (1 << b) < i; b++) 
       ; 
      nalloc = 1 << b; 
      LgiAssert(nalloc >= i); 
     } 

     if (nalloc != alloc) 
     { 
      Type *np = (Type*)malloc(sizeof(Type) * nalloc); 
      if (!np) 
      { 
       return false; 
      } 

      if (p) 
      { 
       // copy across common elements 
       memcpy(np, p, min(len, i) * sizeof(Type)); 
       free(p); 
      } 
      p = np; 
      alloc = nalloc; 
     } 

     if (i > len) 
     { 
      // zero new elements 
      memset(p + len, 0, sizeof(Type) * (i - len)); 
     } 

     len = i; 
    } 
    else 
    { 
     if (p) 
     { 
      int Length = len; 
      for (uint i=0; i<Length; i++) 
      { 
       p[i].~Type(); 
      } 
      free(p); 
      p = 0; 
     } 
     len = alloc = 0; 
    } 

    return true; 
} 

// si ottiene il punto

Inoltre, operator= dovrebbe prendere un const GArray<Type>& per indicare che il lato destro non cambia.

+0

Vorrei dividere decl e defn MA questo significa un sacco di problemi con problemi di link time e dove si istanziano diversi usi della classe.Io ho fatto con alcune delle mie classi template più grandi ed è doloroso. – fret

0
2>lgi8d.lib(Lgi8d.dll) : error LNK2005: "public: int __thiscall GArray<char *>::Length(void)" ([email protected][email protected]@@QAEHXZ) already defined in FrameStore.obj 
2>D:\Home\matthew\network_camera\src\vod_test\Debug\vod_test.exe : fatal error LNK1169: one or more multiply defined symbols found 

lgi8d.lib e vod_test.exe sono due binari separati. Il problema potrebbe risiedere nel fatto che lo .lib definisce già il simbolo che l'exe sta definendo di nuovo.

+0

Questo è quello che pensa il linker, ma sto usando questa classe (con il stesso parametro di tipo) ovunque, sia in EXE che in DLL ed è proprio questa una linea che rappresenta un problema.Ad esempio se uso lo stesso metodo più avanti nel file funziona ok WTH? – fret

+0

Potrebbe essere a causa del nome del maneggevolezza del compilatore es. Il nome "? Lunghezza @? $ GArray @ PAD @@ QAEHXZ" viene generato una seconda volta. –

+0

Questo potrebbe far apparire alcune risposte: http://stackoverflow.com/questions/75486 5/view-compiler-mangled-names-in-c –

0

Si potrebbe provare ad aggiungere un declspec (dllexport) alla classe nella DLL e declspec (dllimport) nel file EXE. Per esempio.

#if !defined(MYDLLEXPORT) 
    // We are users of, and *importing* the library routines... 
    #define MYLIB_SPEC __declspec(dllimport) 
#else 
    // We are building and exporting the library routines... 
    #define MYLIB_SPEC __declspec(dllexport) 
#endif 

// ... 

template<typename Type> 
class MYLIB_SPEC GArray // ... 

Poi assicurarsi MYDLLEXPORT è definita nel progetto che costruisce la DLL e non definito per il file EXE.

AFAIK normalmente non è necessario questo per i modelli.

Ulteriori informazioni su declspec sono qui: http://msdn.microsoft.com/en-us/library/a90k134d(VS.80).aspx.

+0

Sì, li uso per tutte le mie classi normali ... – fret

0

Perché non utilizzare std :: vector?

+1

Ho cercato di mantenere le dimensioni di download e l'elenco delle dipendenze al minimo per anni. E non userei la maggior parte di STL. Ho appena compilato STLport per vedere quali sono le dimensioni della DLL ... circa 805kb, rispetto al mio intero download del software di 1 MB. Potrei abbandonare le mie cose alla fine ... vale la pena di consi – fret

+0

T in STL sta per "modello". Se non installi un determinato modello nel tuo codice, non ti costa nulla. Quindi non sono sicuro da dove vengano questi 805kb. –

+1

L'uso del vettore è buono, ma non affronta la domanda "da dove provengono i miei simboli con più simboli?" – Clay

Problemi correlati