views:

99

answers:

3

I still can't get it to work..

After I had to separate implementation from definition of one of my generic classes because of a forward declaration, i now get Linker errors when trying to build.

IShader.h:

template <typename ShadeType> 
class IShader {

public:
template <typename T>
bool getProperty(const std::string& propertyName, ShaderProperty<T>** outProp);
};

so it's a generic class with a generic member function with a different generic type

implementation looks like this:

#include "MRIShader.h"

template <typename ShadeType> template <typename T>
bool IShader<ShadeType>::getProperty(const std::string& propertyName, ShaderProperty<T>** outProp){
    std::map<std::string, IShaderProperty* >::iterator i = m_shaderProperties.find(propertyName);
    *outProp = NULL;
    if( i != m_shaderProperties.end() ) {
     *outProp = dynamic_cast<ShaderProperty<T>*>( i->second );
     return true;
    }
    return false;
}

but i get Linker errors like this one:

error LNK2001: Nicht aufgelöstes externes Symbol ""public: bool __thiscall IShader<class A_Type>::getProperty<bool>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class ShaderProperty<bool> * *)" (??$getProperty@_N@?$IShader@VA_Type@@@@QAE_NABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PAPAV?$ShaderProperty@_N@@@Z)".   SceneParser.obj

for bool, but also for many other types. I already read through this article: http://www.parashift.com/c++-faq-lite/templates.html

but adding

templace class IShader<bool>;

in the end of IShader.cpp doesn't solve the problem. I also tried to add the 'export' keyword before the keyword 'template' in the implementation, but this just gives me a syntax error.

What's the proper way to declare and implement my template class, which has templated member methods (with a different template type!) in separate files (.h and .cpp) without getting Linker errors?

thanks!

+1  A: 

Your only providing a linkable object for the class in the translation unit, but the member function is templated on another parameter and has also to be explicitly specified.

I don't know if there is a more elegant way, but one compilable version would be:

template bool IShader<bool>::getProperty<bool>
                 (const std::string& propertyName, 
                  ShaderProperty<bool>** outProp);

VC8 and GCC 3.4 both allow to leave out the <bool> after the function name. I'm however not sure about the correct syntax in that case.

But as long as you're not having problems with the compilation time in a big project, save yourself the trouble and put the method definitions (marked inline) in a header.
If you're just worried about the size of the header file, move the definitions in another header file, e.g. IShaderImpl.h, that you include at the end of IShader.h.

Quick sample to remove doubts:

// IShader.h
template<typename T>
struct ShaderProperty {};

template <typename T> 
class IShader {
public:
    template <typename U>
    void getProperty(ShaderProperty<U>**);
};

// IShader.cpp
#include <iostream>
#include "IShader.h"

template<typename T> template<typename U>
void IShader<T>::getProperty(ShaderProperty<U>**) 
{ std::cout << "moo" << std::endl; }

template class IShader<bool>;
template void IShader<bool>::getProperty(ShaderProperty<bool>**);

// main.cpp
#include "IShader.h"
int main() {
    IShader<bool> b;
    ShaderProperty<bool>** p;
    b.getProperty(p);
}
Georg Fritzsche
so I have to do this for any combination of the two template parameters? are you doing this in .h or in .cpp?
Mat
In the .cpp, just put it after `templace class IShader<bool>;`.
Georg Fritzsche
And yes, as far as i see - but i haven't really tried to force template instantiations into source files. Do you really have issues with your compilation time or why are you doing that?
Georg Fritzsche
but wouldn't be the idea of a generic class that it can be used with an arbitrary type?
Mat
Yes, but you're trying to put the definition of a template function into a source, i.e. hide it from the other source files. You can either provide them a full inline definition in the header or you put it in a source file and force explicit instantiations for every type needed.
Georg Fritzsche
The compiler needs to generate code somewhere you can link to and for that it obviously needs the definition. It also can't just generate it for every possible type in the universe.
Georg Fritzsche
gf is right. You're asking the impossible. Templates are not classes. They can be used to *generate* classes, but only if the full template definition is visible at the place where it must be instantiated. If you need a `IShader<bool>`, then either the full template definition must be visible (by including it in a header, as is the usual solution), or a declaration must be visible, and a .cpp file somewhere must provide the explicit instantiation for `bool` so that the code is generated there, and can be linked in at the use site.
jalf
Without that, the `IShader` template is never instantiated for a `bool` argument. The only place were able to instantiate it (in ishader.cpp), doesn't know which types it should be instantiated for. And in the place where we know it should be instantiated for bool, we can't see the actual definition to use in the instantiation
jalf
A: 

Did you include the cpp file from the header file?

http://www.parashift.com/c++-faq-lite/templates.html#faq-35.13

Notice that foo-impl.cpp #includes a .cpp file, not a .h file. If that's confusing, click your heels twice, think of Kansas, and repeat after me, "I will do it anyway even though it's confusing." You can trust me on this one. But if you don't trust me or are simply curious,

EDIT I got this to compile:

//////////////////////////////////////////////////////////////////////////
// Foo.cpp - implementation of Foo

#include "stdafx.h"

template <typename ShadeType> 
template <typename T>
bool IShader<ShadeType>::getProperty(ShaderProperty<T>** outProp){
    *outProp = dynamic_cast<ShaderProperty<T>*>( NULL );
    return false;
}

//////////////////////////////////////////////////////////////////////////
// Foo.h 

template<typename T> class ShaderProperty
{
};


template <typename ShadeType> 
class IShader {
public:
    template <typename T>
    bool getProperty(ShaderProperty<T>** outProp);
};

#include "Foo.cpp"

///Main.cpp

IShader<someclass> blah;
ShaderProperty<someclass2>* prop;
blah.getProperty(&prop);

So in other words you can't really hide your implementation code from the library users. You either have the code in the header file or include the cpp file WITH the header file.

Igor Zevaka
Well, thats basically the same as putting it all in the header. You can hide the definitions though as long as you *explicitly instantiate* every class and function.
Georg Fritzsche
Exactly, which makes the whole templating just about useless.
Igor Zevaka
Not if you only specialize on a limited number of types and use the class in many translation units in a big project - it really saves compilation time in such cases. But i have to admit, its unlikely that the OP has that problem. But... saying you can't hide the definitions is wrong - you can.
Georg Fritzsche
A: 

Templated classes in C++ must provide the declaration and implementation in the same file. Otherwise when another file includes the header, the linker won't be able to find the definition of those probably.

Andy
No, you can force explicit instantiation in source file for the complete type. See e.g. http://www.comeaucomputing.com/techtalk/templates/#export
Georg Fritzsche
how can I force this? the 'export' keyword is not allowed with templates in VisualStudio C++
Mat
By doing exactly the `templace class IShader<bool>;` you're forcing a instanciation of a template class that other objects can link to.
Georg Fritzsche