I am completely new to C++\CLI and I have the following problem: I have a class called, for example, MyClass, a few values of which I need to store in memory. I have decided to use List, because it is the most familiar to me, since I was using C# for a long time. So here goes the code:
//Header File
ref class MyClass
{
public:
MyClass(void);
private:
System::Collections::Generic::List<MyClass^> values;
};
//CPP file
MyClass::MyClass(void){
this->values=gcnew System::Collections::Generic::List<MyClass^>();
}
The compiler keeps on saying that there's an error C2582. But if there's no assignment operator, then how should I initialize the List?
Thanks in advance.
values should not be a List<...^>, but a List<...^>^. You want to assign a reference, not to create another clone of the list.
Related
When writing this example into the IDE:
enum class EnumTest {
FOO, BAR;
lateinit var foobar: String
}
I get the message With old Native GC, variable in enum class can't be changed after initialization on the "foobar" variable. I can't find anything about this message online. Also everything seems to work just fine, so my question is:
When and how does this affect me?
Thank you!
You could declare your values in a singleton object, set them there and reference it in the enum.
object Constants {
var FOO_STR = "bla"
var BAR_STR = "bar"
}
enum class EnumTest(val foobar: String) {
FOO(Constants.FOO_STR),
BAR(Constants.BAR_STR);
}
This code should work but also smells fishy.
Since an enum has an immutable flavor by design, I would not recommend this approach. I feel bad about posting this... but if your main problem is the resource heavy loading of these constants, maybe you do not need an enum to store those properties. Maybe just think of a simple singleton container with simple (named) properties.
I have unmanaged object of WtfClass.
class WtfClass { };
And I also have managed class which uses pointer to this object.
ref class MyClass //works fine if you remove "ref"
{
public:
void MyMethod();
void WtfMethod(void * pVoid);
WtfClass *pWtfStruct;
};
void MyClass::MyMethod()
{
/*WtfClass* pWtfStruct; //if you uncomment this it will compile even with ref*/
WtfMethod((int*)(&pWtfStruct)); //(!!!invalid type conversion here)
}
void MyClass::WtfMethod(void *pVoid)
{}
I can't cast WtfClass* pointer from field, but can easily cast the same pointer defined within MyMethod(). If make MyClass unmanaged it works in any case.
It's better to look at screenshots:
https://ibin.co/2iOcN1ooaC7A.png [using ref-bad.png]
https://ibin.co/2iOcYtP84H0e.png [using ref-good.png]
ibin.co/2iOcjCCc2gQe.png [without ref.png] (sorry not enough reputation to paste more than 2 links)
Of course I can have workaround like this, but I'd like to understand why this happening:
void MyClass::MyMethod()
{
WtfClass* pWorkAround = pWtfStruct; //not required in this case
WtfMethod((void*)(&pWorkAround));
}
OK, so to summarize, without the duplicate field & local variable names:
ref class MyClass
{
WtfClass* fieldWtfPtr;
void foo()
{
WtfClass* localvarWtfPtr;
WtfMethod((int*)(&fieldWtfPtr)); // Error
WtfMethod((int*)(&localvarWtfPtr)); // Works
}
};
Side question: &fieldWtfPtr is of type WtfClass**, a double pointer. Did you mean to cast that to a int**, also a double pointer? Or perhaps did you want to take fieldWtfPtr as a WtfClass* single pointer and cast that to a int* single pointer?
Here's why you're getting the error: MyClass is a managed object. The garbage compiler is allowed to move it around at any point, without telling you. So, it's location in memory can change at any point. So when you try to take the address of a class field, it's not valid because the address of that field can change at any point!
Why the other things make it work:
Local variables are stored on the stack, and the stack doesn't get moved around by the garbage collector, so it is valid to take the address of a local variable.
If you remove the ref, then MyClass is no longer a managed object, so the garbage collector won't move it around, so now the addresses of its fields won't change willy-nilly.
For this case, the easiest fix would be to make use of a local temporary variable.
void foo()
{
WtfClass* localCopyWtfPtr = this->fieldWtfPtr;
WtfMethod((int*)(&localCopyWtfPtr)); // Works
// If WtfMethod changed the data, write it back.
this->fieldWtfPtr = localCopyWtfPtr;
}
When I tried to recreate this, the compiler generated the following error:
error C2440: 'type cast' : cannot convert from 'cli::interior_ptr<CWtfClass*>' to 'LPVOID *'
I think what is going on here is some magic that allows managed classes to have unmanaged members. The MSDN documentation for cli::interior_ptr describes what's going on - basically this is used to allow for the managed object to change its memory address in the managed heap, which would cause problems when native pointers come in to play.
The reason that assigning the member to a variable first works is most likely because it has an implicit conversion to the template parameter, but since it is a managed type the compiler won't allow you to get the address of the variable (since the garbage collector can move it around in memory as needed).
The workaround in your question is probably the best way to fix this compiler error.
David answered why this happens and suggested a workaround for your case.
I'll just post a different solution here: You can pin your managed object to tell the GC not to move it around. The most lightweight way to do that is through pin_ptr (the GC won't even know you pinned something unless it stumbles upon your code in the middle of a collection). As long as it stays in scope, the managed object will be pinned and won't move. It's best if you avoid pinning for too long, but this lets you get a pointer to a chunk of managed memory which is guaranteed not to move - it's helpful when you want to avoid copying things around.
Here's how to do it:
pin_ptr<WtfClass*> pin(&pWtfStruct);
WtfMethod(pin);
pin acts just like a WtfClass**.
Regarding side question of David Yaw.
I faced with this problem while used some WINAPI functions.
IAudioEndpointVolume* pWtfVolume = NULL;
pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&pWtfVolume);
pWtfVolume->SetMute(BST_CHECKED, pGuidMyContext);
And it's working only if I pass &pWtfVolume. Ironically you can pass argument without "&", just pFieldVolume and compiler will say OKAY, but interface IAudioEndpointVolume will not work.
Look at this:
ref class MyClass
{
WtfClass* fieldWtfPtr;
void foo()
{
WtfClass* localvarWtfPtr;
WtfMethod((int*)(&fieldWtfPtr)); // Error
WtfMethod((int*)(&localvarWtfPtr)); // Works
WtfMethod((int*)(fieldWtfPtr)); // Compiles!!!
}
};
I am missing something ...
I have a class SocketComm. I have a derived class SocketTCP : public SocketComm.
SocketComm has the following in it:
static SocketComm *Instance; // static pointer to the singleton instance du jour
I am trying to define a method in SocketComm as follows:
static inline SocketTCP *GetTCPclass()
{
// consistent method to return a SocketTCP* cast of SocketComm::Instance (or NULL)
SocketTCP *s = dynamic_cast<SocketTCP *>(Instance);
assert( s != NULL );
return s;
}
I am getting (VS 2010)
error C2680: 'SocketTCP *' : invalid target type for dynamic_cast
What am I missing? SocketComm is abstract -- is that the problem?
The answer seems to be that dynamic_cast needs a full declaration of the target class.
See my comment above.
Yeah, like #ViRusTriNiTy says, I suppose I could have put it in SocketTCP as a static inline. That would probably have worked too. But it seems more logical to me in the base class, and there is no performance reason to make it inline. The compiler will inline it when it can (in SocketComm.cpp) anyway.
According to your comments you are mixing code that belongs to a derived class into a base class. This is the wrong approach and you now see why: SocketTCP' : class must be defined before using in a dynamic_cast.
Just approach this in a different way like say adding a separate helper class that does the dynamic_cast.
Jumping to Visual Studio 2015 from Visual Studio 2013, I've noticed some differences in how static self-instances in managed C++ classes are accepted by the compiler. Consider these two examples:
Method 1:
public ref class CResourceManager
{
public:
static property CResourceManager^ Instance
{
CResourceManager^ get() { return %m_Instance; }
}
private:
static CResourceManager m_Instance;
};
Method 2:
public ref class CResourceManager
{
public:
static property CResourceManager^ Instance
{
CResourceManager^ get() { return m_Instance; }
}
private:
static CResourceManager^ m_Instance = gcnew CResourceManager;
};
Method 1 used to work on 2013, but it's failing to compile on 2015. I unfortunately do not have the exact compiler error handy, but it was one of those "Missing semicolon before variable name" errors, basically saying it couldn't find the type CResourceManager (pointing to the static variable declaration).
So on to my questions:
Is method 1 supposed to work or be valid in managed C++?
Why would the second method work in 2015, but not the first (i.e. what are the differences)?
Which method is the proper way to accomplish the end goal?
Method 2 is the proper way to do it. The code you have listed is the equivalent of the C# idiom.
Method 1 is a bit unusual.
The lack of a ^ on a declaration would normally mean that the variable is not allocated on the managed heap. However, since it's a static class member, I'm not sure where it actually gets created.
% is normally used for declaring tracking references, the equivalent of passing a variable by ref or out in C#. To be honest, I didn't think that applying % to a variable without either ^ or % and taking the result as a ^ was even valid. (Though considering the 2015 compiler rejects it, it may not be.)
Even if Method 1 is valid, I'd still go with Method 2: The storage location of m_Instance and how it's returned are both plain, common, and easy to understand. This beats having to think about how the code works any day.
I have coded the following, and am very new to c++, and it feels clumsy. I am trying to give 'spriteBatch' (a unique_Ptr) class scope. Here's the header file:
ref class CubeRenderer : public Direct3DBase
{
public:
CubeRenderer();
~CubeRenderer();
private:
std::unique_ptr<SpriteBatch> spriteBatch;
};
Then in the cpp file Constructor, this:
std::unique_ptr<SpriteBatch> sb(new SpriteBatch(m_d3dContext.Get()));
spriteBatch = std::move(sb);
It just seems clumsy the way I had to create 'sb' and move it to 'spriteBatch'. attempting to assign directly to 'spriteBatch' failed (maybe I simply don't know the proper syntax). Is there a way to avoid needing to use 'sb' & std::move?
Thank you.
The following should work fine:
spriteBatch = std::unique_ptr<SpriteBatch>(new SpriteBatch(m_d3dContext.Get()));
Alternatively, you can avoid repeating the type name with some make_unique function.
spriteBatch = make_unique<SpriteBatch>(m_d3dContext.Get());
There's also the reset member:
spriteBatch.reset(new SpriteBatch(m_d3dContext.Get()));
But, since you mention a constructor, why not just use the member initialization list?
CubeRenderer::CubeRenderer()
: spriteBatch(new SpriteBatch(m_d3dContext.Get())) {}