Template Code Instantiation

Hi ,

I have a below Template based code in List.h file : I have pasted the important template call code calls . This template code has implicit template Instantiation code : #include<List.c> with body of template class code.

Currently we are using IBM AIX 6.1 & we are porting this code in Linux .
In AIX , there is no compilation or Link error since we are using XLC compilets -qtempinc flag . Perhaps it handles the template code instantiation.

The strange thing is in Linux , when this List.h is compiled in an shared archive (.a) , we get lot of redefinition errors :

e.g :

/home/dev11i/inc/List.c:3: error: redefinition of 'int Listiter<T>::replace_next(const T&)'
/home/dev11i/inc/List.c:3: error: 'int Listiter<T>::replace_next(const T&)' previously declared here
/home/dev11i/inc/List.c:13: error: redefinition of 'void Listiter<T>::insert_prev(const T&)'
/home/dev11i/inc/List.c:13: error: 'void Listiter<T>::insert_prev(const T&)' previously declared here
/home/dev11i/inc/List.c:21: error: redefinition of 'int Const_listiter<T>::peek_next(T&) const'
/home/dev11i/inc/List.c:21: error: 'int Const_listiter<T>::peek_next(T&) const' previously declared here

If I remove #include<List.c> , complile the shared archieve successfully and then build a binary using the .a file , we get Link error in main binary using the .a archieve for undefined error for same above Template Member function like replace_next(), insert_prev etc:

e.g :
/home/dev11i/src/gfClClHier.cpp:1883: undefined reference to `Const_listiter<GfCalOvr>::peek_next(GfCalOvr&) const'
/home/dev11i/src/gfClClHier.cpp:1908: undefined reference to `Listiter<GfCalOvr>::insert_prev(GfCalOvr const&)'
/home/dev11i/src/gfClClHier.cpp:1930: undefined reference to `Listiter<GfCalOvr>::replace_next(GfCalOvr const&)'



Please refer this below List.h with template declaration and a short reduced list.c file with template body definition . Please Let us know iF Linus has has g++ or gcc special flag for Template code instantiation.

****List.h******
#ifndef LISTH
#define LISTH

#include <new.h>
#include <Pool.h>
#include <stdio.h>
class lnk_ATTLC;
class Lizt_ATTLC;
class ostream;

typedef void* voidP;

// implements two-way pointers

struct lnk_ATTLC {
lnk_ATTLC* nxt;
lnk_ATTLC* prv;

lnk_ATTLC() {}
virtual ~lnk_ATTLC();

void init(lnk_ATTLC* p, lnk_ATTLC* s) { prv = p; nxt = s; }
virtual lnk_ATTLC* copy();
virtual int operator==(lnk_ATTLC&);
};


// base class for all [Const_]Listiter(T)'s

class Liztiter_ATTLC {

friend class Lizt_ATTLC;

Liztiter_ATTLC(Lizt_ATTLC* lp) : theLizt(lp), cache(0), nextIt(0) {}

protected:
Lizt_ATTLC* theLizt; // associated List
Liztiter_ATTLC* nextIt; // next on chain
lnk_ATTLC* cache; // a recently retrieved link
int cacheNo; // its index or garbage if cache == 0
int index; // current position
lnk_ATTLC* pred; // current position

lnk_ATTLC* getAt(int);
lnk_ATTLC* next();
lnk_ATTLC* prev();
inline lnk_ATTLC* peek_next() const;
inline lnk_ATTLC* peek_prev() const;
void insert_prev(lnk_ATTLC*);
void insert_next(lnk_ATTLC*);
lnk_ATTLC* remove_prev();
lnk_ATTLC* remove_next();
void reset0();

public:

Liztiter_ATTLC(Lizt_ATTLC&);
Liztiter_ATTLC(const Liztiter_ATTLC&);
~Liztiter_ATTLC();

void reset(unsigned = 0);
void end_reset(unsigned = 0);
int at_head() const { return index == 0; }
int at_end() const;
};



class Lizt_ATTLC {
friend class Liztiter_ATTLC;

protected:
lnk_ATTLC* t; // tail
int sz; // number of elements
Liztiter_ATTLC* iter_head;

Lizt_ATTLC();
Lizt_ATTLC(const Lizt_ATTLC& x) : iter_head(0) { init_all(x); }
Lizt_ATTLC(const Lizt_ATTLC&, const Lizt_ATTLC&);
~Lizt_ATTLC();

public:
Lizt_ATTLC& operator=(const Lizt_ATTLC&);

};
template <class T>
class List : public Lizt_ATTLC {

List(const List<T>& a0, const List<T>& a1)
: Lizt_ATTLC((Lizt_ATTLC&)a0, (Lizt_ATTLC&)a1) {}
List(const List<T>&, const T&);

protected:
T* getAt(int);

public:
List();//{printf("cout const called");}
List(const List<T>&);
List(const T&);
List(const T&, const T&);
List(const T&, const T&, const T&);
List(const T&, const T&, const T&, const T&);
~List(); //{}
}
ostream& print(ostream&) const;
};

template <class T>
class Const_listiter : public Liztiter_ATTLC {

protected:
T* getAt(int i);

public:
Const_listiter(const List<T>&);
Const_listiter(const Const_listiter<T>&);
~Const_listiter();

Const_listiter<T>& operator=(const Const_listiter<T>& l) {
return (Const_listiter<T>&) ((Liztiter_ATTLC&)*this =
(const Liztiter_ATTLC&)l);
}

int operator==(const Const_listiter<T>& l) const {
return (const Liztiter_ATTLC&)*this == (const Liztiter_ATTLC&)l;
}
int operator!=(const Const_listiter<T>& l) const {
return (const Liztiter_ATTLC&)*this != (const Liztiter_ATTLC&)l;
}

int find_next(const T&);
int find_prev(const T&);
int next(T& t);
int next(T*& t);
T* next();
int prev(T& t);
int prev(T*& t);
T* prev();

int step_next() {
return Liztiter_ATTLC::next() != 0;
}
int step_prev() {
return Liztiter_ATTLC::prev() != 0;
}
int peek_next(T&) const;
int peek_next(T*&) const;
T* peek_next() const;
int peek_prev(T&) const;
int peek_prev(T*&) const;
T* peek_prev() const;

const List<T>* the_list() {
return (const List<T>*)theLizt;
}
};
^L
template <class T>
class Listiter : public Const_listiter<T> {

public:
Listiter(List<T>&);
Listiter(const Listiter<T>&);
~Listiter();

Listiter<T>& operator=(const Listiter<T>& l) {
return (Listiter<T>&) ((Liztiter_ATTLC&)*this =
(Liztiter_ATTLC&)l);
}

int operator==(const Listiter<T>& l) const {
return (Liztiter_ATTLC&)*this == (Liztiter_ATTLC&)l;
}
int operator!=(const Listiter<T>& l) const {
return (Liztiter_ATTLC&)*this != (Liztiter_ATTLC&)l;
}

List<T>* the_list() { return (List<T>*)theLizt; }

// the following operations change the container
int remove_prev();
int remove_next();
int remove_prev(T&);
int remove_next(T&);
void insert_prev(const T& x);
void insert_next(const T& x);
int replace_prev(const T&);
int replace_next(const T&);
};

#if (defined(__edg_att_40) || defined(__GNUG__)) && !defined(__IMPLICIT_INCLUDE)
#include "List.c"
#endif

#ifndef __USE_STL_TEMPINC__
#include "List.c"
#endif

#endif

****************************** End of List.h file **************

*********************************Start of List.c file ***********
/>>cat List.c
template <class T>
int
Listiter<T>::replace_next(const T& x)
{
if (this->at_end())
return 0;
else
return (((lnnk_ATTLC<T>*)Liztiter_ATTLC::peek_next())->val=x,1);
}

template <class T>
void
Listiter<T>::insert_prev(const T& x)
{
Liztiter_ATTLC::insert_prev(lnnk_ATTLC<T>::getnewlnnk_ATTLC(x));
}


template <class T>
int
Const_listiter<T>::peek_next(T& t) const
{
if ( at_end() )
return 0;
else
return (t = ((lnnk_ATTLC<T>*)Liztiter_ATTLC::peek_next())->val, 1);
}
****************************** End of List.c file **************
you've got build errors, provide your build command.
the client code would also be needed.


1
2
3
4
5
6
7
#if (defined(__edg_att_40) || defined(__GNUG__)) && !defined(__IMPLICIT_INCLUDE)
#include "List.c"
#endif

#ifndef __USE_STL_TEMPINC__
#include "List.c"
#endif 
please, explain


by the way, the .c suffix is used for C code, not C++
Last edited on
Hi ,

Yes, the concern regarding List.c suffix seems questionable, but if we observe the difference, the file ".c" is #included in a ".h" file which is a handled during preprocessing phase of the gnu c compiler. The .h file internally will inflate all preprocessor directives having the code of List.c and ".h" file is actually included (#include<List.h> ) in application code file gfClApi.cpp.
So filename or suffix in header file is not considered.

If I have List.h (#include<List.h> ) within List.c file (the default way ) and if I try to directly compile List.c with gcc/g++ , I get the anticipated errors.


Below is the compilation rule : -

g++ -fpermissive -c -g -m32 -DGFCAL_CLIENT_TEST -DUSENODCE -DAIX_MIGRATION -I/usr/include/ -I/usr/include -I/usr/include/c++/4.4.4/ -I/usr/include/c++/4.4.4/x86_64-redhat-linux/32/ -I/xenv/OracleSDK/X/11.2.0.4//precomp/public -I/xenv/OracleSDK/X/11.2.0.4//rdbms/demo -I/xenv/OracleSDK/X/11.2.0.4//rdbms/public -I/home/dev11i/inc -I/wrk/ctdtdev/UTILS/gsoap-2.8/gsoap /home/dev11i/src/gfClApi.cpp -o /home/dev11i/libGfCl.a_dir/obj/gfClApi.o

In file included from /home/dev11i/inc/gfClLlIds.hpp:21,
from /home/dev11i/inc/gfClClHLs.hpp:38,
from /home/dev11i/src/gfClApi.cpp:76:
/home/dev11i/inc/List.h:291: warning: friend declaration 'void voidP_List_sort_internal(List<T>&, int (*)(const T&, const T&))' declares a non-template function
/home/dev11i/inc/List.h:291: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
/home/dev11i/inc/List.h:337: warning: friend declaration 'void voidP_List_sort_internal(List<T>&, int (*)(const T&, const T&))' declares a non-template function
In file included from /home/dev11i/inc/List.h:672,
from /home/dev11i/inc/gfClLlIds.hpp:21,
from /home/dev11i/inc/gfClClHLs.hpp:38,
from /home/dev11i/src/gfClApi.cpp:76:
/home/dev11i/inc/List.c:3: error: redefinition of 'int Listiter<T>::replace_next(const T&)'
/home/dev11i/inc/List.c:3: error: 'int Listiter<T>::replace_next(const T&)' previously declared here
/home/dev11i/inc/List.c:13: error: redefinition of 'void Listiter<T>::insert_prev(const T&)'
/home/dev11i/inc/List.c:13: error: 'void Listiter<T>::insert_prev(const T&)' previously declared here
/home/dev11i/inc/List.c:21: error: redefinition of 'int Const_listiter<T>::peek_next(T&) const'
/home/dev11i/inc/List.c:21: error: 'int Const_listiter<T>::peek_next(T&) const' previously declared here
make: *** [gfClApi.o] Error 1
The presence of multiple redefinition errors arising in List.c suggests that List.c is being included more than once.
Did you really mean to allow for that possibility?
1
2
3
4
5
6
7
#if (defined(__edg_att_40) || defined(__GNUG__)) && !defined(__IMPLICIT_INCLUDE)
#include "List.c"
#endif

#ifndef __USE_STL_TEMPINC__
#include "List.c"
#endif  


When you use the preprocessor, you should try to ensure that all cases are covered:
1
2
3
4
5
6
7
8
# if (defined(__edg_att_40) || defined(__GNUG__)) && !defined(__IMPLICIT_INCLUDE)
#   include "List.c"
/* Now the cases are mutually exclusive */
# elif ! defined __USE_STL_TEMPINC__
#   include "List.c"
# else /* Usually add an 'else' branch. */
#   error "Incompatible or missing macro definitions" /* TODO: improve the error message */
# endif 

If the redefinition errors arise again, make sure that List.c isn't being compiled as C++ and linked in. Usually template definitions go into files with a *.tpp or *.ipp extension.

Why do you want to conditionally include template definitions?
That suggests there's an alternative implementation selected elsewhere. That's a linker error waiting to happen, when a better alternative might be to issue an #error.

if I try to directly compile List.c with gcc/g++ , I get the anticipated errors.

Did you tell the compiler it is C++ code? -xc++

This line:
#include<List.h>
Search local include files first: # include "List.h"

It is illegal to use names that begin with an underscore followed by a capital letter (e.g., _List_sort_internal). Those are reserved for use by the implementation.
http://eel.is/c++draft/lex.name#3



Last edited on
Thanks a lot ,

commenting the code block in List.c file in Linux platform did the work :
#ifndef __USE_STL_TEMPINC__
#include "List.c"
#endif

The issue seems like this : In AIX ,macro __USE_STL_TEMPINC__ was not defined and hnece List.c was included.
In AIX , the template code declaration had its respective body and hence all symbols resolved.

In Linux : __GNUG__ is implicitly defined by Linux platform headers and __USE_STL_TEMPINC__ is not defined by default , so List.c was included twice leading to duplicate symbols.

Just FYI , the other code issue is ignored since its is a Legacy code for String class implementation downloaded from internet.

Topic archived. No new replies allowed.