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;
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
// 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&);
};
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.
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?
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
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.