1
votes

I've got a problem with passing templates (I think that's the cause) from the CLI project.

I've got 3 layers of classes (for encapsulation reasons and technical reasons).

I'll show an example of the buggy part (please no comments about the encapsulation reasons, they're not demonstrated here)

Class A is in a C++ DLL :

class A {
public:
    template<class T>
    void foo(T blah) { //Do stuff }
}

Class B wraps class A (also regular non-ref class):

class B {
public:
    template<class T>
    void foo(T blah) { a->foo(blah); }
private:
    A* a;
}

Class C is a ref class, which calls class B with an explicit type :

ref class C {
public:
    void foo(int blah) { b->foo(blah); }
private:
    B* b;
}

They compile alright (.obj is created), but the linker doesn't link the objects correctly.

I get 2 linker errors for the method:

error LNK2028: unresolved token (0A000645) "public: void __cdecl B::foo(class utils::CustomString const &,int const &)" (??$foo@_N@B@Namespace@@$$FQEAAXAEBVCustomString@utils@@AEB_N@Z) referenced in function "private: void __clrcall C::foo(int)" (??$foo@_N@Namespace@@$$FAE$AAMXPE$AAVString@System@@_N@Z)

error LNK2019: unresolved external symbol error LNK2019: unresolved external symbol "public: void __cdecl B::foo(class int const &)" ??$foo@_N@B@Namespace@@$$FQEAAXAEBVCustomString@utils@@AEB_N@Z) referenced in function "private: void __clrcall C::foo(int)" (??$foo@_N@Namespace@@$$FAE$AAMXPE$AAVString@System@@_N@Z)

Edit

I don't have the lines with me now (not on same PC) but it says it couldn't link B.foo referenced in C.foo

I'm compiling the ref class with /clr in debug mode /MDd (yes it has to be in debug mode because of other dependencies that are all compiled the same way)

Anyone knows why this is happening ? And more important: how to solve this ?

Edit:

When setting class B (the wrapper) to be compiled with /GL (Whole Program Optimization) I get a different error:
LNK2001:

error LNK2001: unresolved external symbol "public: bool __cdecl Interface::B::foo(int &)const " (??$foo@_J@B@Namespace@@$$FQEBA_NAEBVCustomString@123@AEA_J@Z)

3
What are the names of the tokens and external symbols? - Andy Finkenstadt
Yes, the full linker error is needed. - C Johnson
Right, I don't have the exact line here with me, I've added a line that i hope will help - Yochai Timmer
I've seen lots of these lines on google, will it help if reconstruct the lines with my current example ? - Yochai Timmer
When you say "Class A is in a C++ DLL", does that mean a separate DLL from classes B and C? - Ben Voigt

3 Answers

1
votes

The problem is a bug with visual studio's linker.

First of all, you need to set the /GL (Whole Program Optimization) flag so it links the template after compile.

Then you need to use one of these work-arounds:

BUG: LNK2001 on Member Function When Use Nested Class Template

0
votes

This is standard lossage with C++ templates, they don't have external linkage like .NET generics. You have to put the template function definition in a header file so you can #include it in the source code file that contains the ref class. Just like your code snippet does. It isn't otherwise specific to C++/CLI.

Be sure to wrap the #include with #pragma managed if you compile the rest of the native classes without the /clr option (like you should). Like this:

#pragma managed(push, off)
#include "FileWithTemplateDefinition.h"
#pragma managed(pop)
-1
votes

These linker errors are pointing out that you are missing a definition somewhere (as opposed to a declaration). Not having access to your code makes this difficult for us to determine, especially without having any verbose linker errors.

[EDIT]

This code compiles and links for me just fine in a managed C++ project:

#pragma managed

class A {
public:
    template<class T>
    void foo(T blah) { int i = 0; }
};

//Class B wraps class A (also regular non-ref class):
class B {
public:
    template<class T>
    void foo(T blah) { a.foo(blah); }
private:
    A a;
};

// Class C is a ref class, which calls class B with an explicit type :
ref class C {
public:
    C() { b = new B(); }
    ~C() {  }
    !C() { delete b; }
    void foo(int blah) { b->foo(blah); }
private:
    B* b;
};