Tuesday, June 10, 2008

What! Are you blind?

I didn't say it out loud, but I was thinking something similar.
Personification of inanimate objects like compilers is daft,
but it feels good. That's my excuse anyway.

Here's the situation.

Given this C++/CLI code:

template< typename T >
ref struct A
{
~A()
{
}
};

ref struct B
{
A<int>^ a;
}

The linker generates this error:
error LNK2020: unresolved token (06000414) A<int>::Dispose

Knowing that in C++/CLI, the destructor syntax generates a Dispose function for ref classes/structs, the usual resolution is to ensure that A class has a destructor defined.

Which is where you say "What! Are you blind?". Because there it is. And no matter how many times you check it to make sure it exists and hit the recompile button, the linker still spits out the same error. I know. I tried a lot of times.

The solution? Change the member declaration in B to:
A<int> a;


The reason? There are conflicting behaviours between the C++ template compilation and the C++/CLI MSIL generator. By declaring a destructor, the MSIL generation insists that the class must have a Dispose method. But the C++ template compilation will not emit definitions of functions in template classes unless the function is actually used.

By using the handle syntax (^), but never manually calling a->Dispose(), the compiler will never emit the Dispose function. Removing the handle syntax, such that the "a" variable exists on the stack (at least notionally), means that behind the scenes the compiler generates the call to a->Dispose() when the variable goes out of scope.

In native C++, the template optimisation is exactly what most programmers want. Why have a function defined if it is never called?

However an MSIL class in an assembly must be fully defined, regardless of what functions are called.

Hence, unless you manually call Dispose, or use the stack based syntax, the compiler never generates the Dispose function for the linker to find, which is invalid for an MSIL class in an assembly.

Of course, the resulting error is useful because I realised that I hadn't managed the object properly. Unfortunately this can only be considered serendipitous, because should many objects of type A exist, but only one of them get Disposed properly (or even improperly), the unresolved external error won't show.

The unanswered question. It seems that there is something special about the Dispose function (or how it is generated with the destructor syntax), because the same is not true of an ordinary function. E.g.

template< typename T >
ref struct A
{
~A()
{
}

void f()
{
}
};

ref struct B
{
A<int> a;
}

This code does not generate an unresolved external error for the f function. Further investigation with ILDasm is required.

Note: This post is relevant to Visual Studio 2005. I am not sure of how VS 2008 behaves.

14 comments:

Rouge said...
This comment has been removed by the author.
Rouge said...

Hi,

thanks for the explanation. This is still valid in VS2008.

However instead of declaring it on the stack one can just define a destructor for the class which includes the object and delete it there, thus the compiler knows it has to build the decompose function.

Cheers,
Martin

Anonymous said...

Also still valid in 2012

wordpress said...

It's very good so i am impressed and ilike to come again in future..

totopickpro said...

I’m shocked why this coincidence did not took place earlier! I bookmarked it.

totosafeguidecom9 said...

Well-written article. I was checking continuously to this website and Im really inspired!

카지노사이트 said...

I definitely enjoyed every little bit of it.

안전놀이터 said...

I really enjoyed it. keep up the good work.

토토사이트 said...

I’m hoping the same high-grade blog post from you in the upcoming also.

파워볼사이트 said...


Thank you I was impressed by your writing.

카지노사이트 said...

Very good article, I enjoyed reading your post.

안전놀이터 said...

i have to say this thing you have did good and nice

토토사이트 said...

I love reading this post. Keep posting more content in this article

파워볼사이트 said...

Your post is very great.i read this post this is a very helpful.