> I've just converted a small DLL from C++Builder to a C++/CLI DLL in vs2005
> that uses singletons with no real problems. It isn't instantiated twice
> for me. The only issue I had was with cleanup. To destroy the singleton
> we use std::atexit to register a cleanup func but in C++/CLI, you have to
> call _onexit_m (found this because of crashes at shutdown).
Hi,
Unfortunately my situation is more complex than that - I'm calling an
existing static C++ class library which is for all purposes too complex to
convert to C++/CLI from a C++/CLI program.
I've managed to reproduce the problem in a small VS2005 project - I've
uploaded it at http://www.aonaware.eclipse.co.uk/temp/SingletonTest.zip if
anyone has the time to look. You can see / debug the constructor of the
singleton being called twice. I think it may have something to do with the
template base class for the singleton but I'm not sure...
Thanks,
Adrian
Tamas Demjen - 13 Jul 2006 23:57 GMT
> I've managed to reproduce the problem in a small VS2005 project - I've
> uploaded it at http://www.aonaware.eclipse.co.uk/temp/SingletonTest.zip if
> anyone has the time to look. You can see / debug the constructor of the
> singleton being called twice. I think it may have something to do with the
> template base class for the singleton but I'm not sure...
That's interesting. I tried your code, and the constructor is really
called twice.
However, I don't understand why your Settings::internalFunction is
static. Since Settings is a singleton class, it's not really needed to
make any of its members static. I modified your code this way:
Settings.hpp:
void internalFunction();
Settings.cpp:
void Settings::internalFunction()
{
getProperty();
}
Wrapper.h:
static void doSomething() {
Settings::instance().internalFunction();
}
And now it works fine, the constructor is only called once.
I also took a look at your project settings, and noticed that your
SingletonTest.lib is NOT compiled with the /clr. Mixing managed and
unmanaged code can somtimes bring up very subtle problems, as you can
read it at Marcus Heege's blog:
http://www.heege.net/blog/default.aspx#ad5e2c3da-ff4d-40bc-bea9-9da8ec9e091e
Read "Avoid native code in managed object files (".obj" files)"
I'm not sure if your problem is related to this in any way, but guess
what. I recompiled your SingletonTest.lib with the /clr settings, then
the rebuilt the dll and the C# code, and the problem disappeared.
So I gave you 2 different workarounds for your problem.
I'm not sure if this glitch is considered a compiler bug, or simply bad
practice, but it certainly looks serious to me.
Tom
Russell Hind - 14 Jul 2006 09:00 GMT
> Read "Avoid native code in managed object files (".obj" files)"
>
[quoted text clipped - 6 lines]
> I'm not sure if this glitch is considered a compiler bug, or simply bad
> practice, but it certainly looks serious to me.
Could it be that the singleton is initialised once for native code and
once from managed code? Perhaps there is an issue with statics called
from both?
Cheers
Russell
Tamas Demjen - 14 Jul 2006 18:13 GMT
> Could it be that the singleton is initialised once for native code and
> once from managed code?
Exactly. Thanks for the hint -- now I see that the same include file was
compiled into managed code in one project, and into unmanaged code in
the another project. Then the two object files were linked together,
which caused one function call to use a different singleton than the
other function call.
Tom
Tamas Demjen - 14 Jul 2006 18:10 GMT
> I've managed to reproduce the problem in a small VS2005 project
I know exactly what's wrong with your project. When you compile a
project with /clr, the default is #pragma managed. When you compile it
without /clr, the default is #pragma unmanaged. Now you can mix the two
things, but you have to make sure that the *same* piece of header file
is compiled with the same settings.
In your project, when you #include "Settings.hpp" and "Singleton.hpp",
the same declaration is compiled as #pragma unmanged in the unmanaged
LIB, and with #pragma managed in the managed DLL. The two things are
*not* byte compatible! You're compiling the same piece of code with
completely incompatible ways, and then link to two things together.
The simplest fix to your problem is to go to Wrapper\Stdafx.h, and
modify it to the following:
#pragma unmanaged
#include "Settings.hpp"
#pragma managed
Please forget my previous message that I sent yesterday. It's not that
it's not correct, but it doesn't show your real problem. Your real
problem is explained in this message.
Just to clarify things. You are not normally required to compile every
native-style class with #pragam unmanaged. You can very easily compile
ISO C++ classes into managed code:
#pragma managed
class NativeClass { }; // generates managed code
However, you're including a non-/clr header file to your /clr project.
You really have to make sure that the compiler settings are matching.
This is the same kind of mistake as putting #ifdef _DEBUG into your
header file, and linking your library project without _DEBUG defined,
but your main application with _DEBUG defined. When the same header file
is compiled with different compiler options, you're calling for trouble.
You can easily lose byte compatibility. This is exactly what happened
with your project -- Singleton.hpp was compiled with one compiler
setting here, and another compiler setting there, and then the two
things were linked together.
Now I can tell for sure that this was definitely not the compiler's
fault. This is something you have to watch carefully when mixing
unmanaged and managed units.
Hope this helps.
Tom
Adrian - 15 Jul 2006 11:22 GMT
> Please forget my previous message that I sent yesterday. It's not that
> it's not correct, but it doesn't show your real problem. Your real problem
> is explained in this message.
Excellent - thank you very much for this post - it explains my problem
exactly.
Thanks again.