Link to home
Start Free TrialLog in
Avatar of gunn
gunn

asked on

C++ Templates in library; how to link?

I have in a static library C++ class template. In an application I am trying to link with that library, I am getting unresolved errors with a particular instance of this template. I figured it was becuase this template was not being instantiated in the library and so it couldn't find the proper code in the library to link with.

I checked the online help of MSVC++5.0 and found that you need to 'explicitly instantiate' the template to work. So, I tried it. And what I have does not work.

I have 2 class templates, and made a .cpp file to instantiate both of them to the relevant types, and included whats shown below.

template  CuListNode<CuChoice>;
template  CuList<CuChoice>;

I get warnings of : no suitable definition provided for explicit template instantiation request for each member function and constructor/destructor in the templates file.


And I get unresolved external symbol errors as such:
 CuList<class CuChoice>::CuList<class CuChoice>(void)(??0?$CuList@VCuChoice@@@@QAE@XZ)

How should I work this out, and what possibly am I doing wrong with the instantiation? I know its a moutful, but I hope someone can help.

Thanks a lot.

Tom
Avatar of mitchell042997
mitchell042997

OK, are you sure you are using templates correctly?

First, in your class defintion, you need to use the template statement.  So, say we are making a class which will do arithmetic operations on numbers.  The numbers could be a float, int, etc.  This is an example of when to use templates, because the actual function code won't be determined until run-time.

So, first, to define our class:

template<class NumType>
class Number {
private:
  NumType data;

public:
  MixedMath();
  ~MixedMath();

  NumType Add(int someInt);
};

Now, your function code must look like this!

template<class NumType>
Number<NumType>::Number()
{  // do constructor business
}

template<class NumType>
Number<NumType>::~Number()
{  // do destructor business
}

template<class NumType>
NumType Number<NumType>::Add(int someNumber)
{
  return data+(NumType)someNumber
}

So see, you need the template<class ...> and then the class name for the template in the class name before the ::.

Then, in your main program, when you create an instantiation of the Number class, you need to define its type.  For example, if you wanted to make it a double, you'd do:

int main() {
  Number<double> myNumber;
...


Does that answer your question?  Happy Programming!
Yes, nietod, that is exactly what I am trying to do. The template definition is buried in a static library I have made. My executable file has nothing to do directly with the templates, but when I link with this library, I get the unresolved errors towards that specific type.

Any fix?
Avatar of gunn

ASKER

I checked the code against what mitchell had said, and its good. The problem has to do excactly as nietod said. The code is an a static library I created, so it doesn't know before-hand how to instantiate itself. I've tried to do it as they say, 'explicitly' but it doesn't seem to work. The .cpp file in the library I'm using to explicitly declare looks like :


----------------------------------

#include "CuList.h"

#include "CuListNode.h"

#include "CuChoice.h"





template class CuListNode<CuChoice>;

template class  CuList<CuChoice>;
-------

but that doesn't seem to do it, as I get the same linker errors + compile warnings like below for each member function:

D:CuTemplates.cpp(11) : warning C4661: 'CuListNode<class CuChoice>::CuListNode<class CuChoice>(const class CuChoice)' : no suitable definition provided for explicit template instantiation request

And the two 'exact' linker errors like (libCXm.lib is my static library):

libCXm.lib(CXmChoiceMenu.obj) : error LNK2001: unresolved external symbol "public: __thiscall CuList<class CuChoice>::CuList<class CuChoice>(void)" (??0?$CuList@VCuChoice@@@@QAE@XZ)

libCXm.lib(CXmChoiceMenu.obj) : error LNK2001: unresolved external symbol "public: virtual __thiscall CuList<class CuChoice>::~CuList<class CuChoice>(void)" (??1?$CuList@VCuChoice@@@@UAE@XZ)

Does that info help any?


Avatar of gunn

ASKER

I have a way to do this for dynamic libraries (DLL's) that works, and should work for a static library.   I don't know that it the best way, I just know that it works, so I'm posting this as a comment in case someone can give you beter advice.

What I do, is in the library create a non-template class that is a derived from the template class instantiated for a particular type.  That's clear isn't it?  an example.  

(the syntax may not be 100% correct, but this should give you the idea)  Say you have an array template class, called Array.  Yo can create two non-template clases one for characters and one for integers as follows.  These clases could be made exportable as appropriate (extern in you case, DllExp in mine)

template <class T> class Array
{
// stuff
};

class IntArray  : public Array<int>;
class CharArray : public Array<char>;

Does that help?
Well, I tried that nietod. The sample file compiled file, but I got the same linker problems, just with that file instead.

I'm stumped. It seems that being in a static library, that particular type doesn't get instantiated, and is then not able to during linking to make an executable.

Theres some info on 'Explicit Instantiation" and a word about templates in libraries in the MSVC++ online help, but I can't get what they have in there to work, which is kind of what I have in my original question.

I'm stumped! Theres got to be someone who uses templates in libraries and gets them to work!! ahhhhhhhh

Avatar of gunn

ASKER

Where (when) do you get the errors?  When compiling the static library or the program that uses it?

Stupid question but.. You did change the program that uses the static library so it now uses the derived classes and not the template classes.  Right?


Post your current code if you can.
I do two things to make this work:

First, create two separate header files for the template class.  One contains the declaration of the template class and its members.  The other contains the *definitions* of the template class members.  It is desirable to separate these so that only the static library sees the member definitions.  Consider the trival template class A declared in "templ.h":

///////////////////////////////////////////////////
// templ.h:  Define a trivial template class
template <class T> class A {
public:
      A();
      ~A();
      T f(T t);
      T m_t;
};
///////////////////////////////////////////////////


... and a definition of its members in templ_def.h:
///////////////////////////////////////////////////
// templ_def.h: Definition of template members, only for library
template <class T> inline A<T>::A() {}
template <class T> inline A<T>::~A() {}
template <class T> inline T A<T>::f(T t) { return t; }
///////////////////////////////////////////////////

... the static library source file "templ.cpp" includes both these headers and explicitly instantiates the template and its members.  templ.cpp is compiled and built into the static library:

///////////////////////////////////////////////////
// templ.cpp:  Instantiate the template for a static library
#include "templ.h"      // Include the template definition
#include "templ_def.h"  // Include member definitions
// Explicitly instantiate the template and its members
template class A<int>;
///////////////////////////////////////////////////

... finally, "use_templ.cpp" includes only the template declaration, but not the member definitions, and links with the library to get the compiled member functions:

///////////////////////////////////////////////////
// use_templ.cpp: Use the template instance defined
// in the static library by templ.cpp
#include "stdio.h"
#include "templ.h"  // Include the template declaration
// ... but don't include the member definitions in templ_def.h
// that prevents this file from "accidentally" instantiating
// a copy of the members

// Instantiate the template without members
// Members should be linked from the library
extern template class A<int>;

void main()
{
      A<int> a;
      printf("A<int>::f(3) == %d\n", a.f(3));
}
///////////////////////////////////////////////////

ASKER CERTIFIED SOLUTION
Avatar of jlilley
jlilley

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 gunn

ASKER

Yoooohooo!

That was it! I changed the *.cpp files of my templates to *_def.h with the definitions and made them inline, and made up a new *_inst.cpp to instantiate the class and built it into the static library. Worked like a charm, no compiler errors...nothing.

Then, linked with my application and walla! No linker error for that anymore....I'm a happy guy now...thanks a lot for everyone's help...especially jlilly! (now, if i can get through these other probs..heheheh, one step at a time.)

Thanks,

Tom