0

I have a code that's a mixture of standard C++ and cli/c++ And i am trying to create the following object:

std::map<std::string, System::IO::StreamWriter> streamWrite;

But it doesn't work. the compiler actually crushes when i try to build the project. Is there a way to make it work?

Edit: my code is originally cli c++ and i am slowly converting it to native c++, and that's why i have a mixture of both native and managed objects. I have no idea yet how to convert StreaReader and StreamWriter objects to native C++ so i am leaving this to the very end, so i n the meantime i have this "strange creature" - std::map that holds a managed object as its value.

Ðаn
  • 10,934
  • 11
  • 59
  • 95
primeQuestion
  • 51
  • 1
  • 9
  • 2
    Possible duplicate of [Mixing managed and unmanaged C++ code?](http://stackoverflow.com/questions/10729008/mixing-managed-and-unmanaged-c-code) – Rakete1111 Apr 25 '17 at 19:34
  • 4
    References to reference types like StreamWriter require using the ^ hat. You cannot store a managed reference in an unmanaged collection like std::map, the garbage collector will not have a chance to find it back. You have to wrap it with `gcroot<>`. Do consider using a `Dictionary` instead. – Hans Passant Apr 25 '17 at 19:43
  • @ Rakete1111 its not. i read it and it didn't help me solve my particular issue. It's to general. Would appreciate help with my particular issue. – primeQuestion Apr 25 '17 at 19:45
  • 1
    What is your *"particular issue"*? You say *"the compiler actually crushes when i try to build the project."*, but what is the error message? We are not witches! – sergiol Apr 25 '17 at 21:53

1 Answers1

0

Standard C++ things (like std::map) and managed objects do not mix out of the box. It is however possible by using GCHandle to get an intptr_t that represents a managed object and keeps it from getting garbage collected.

intptr_t GetHandle(System::Object^ obj) {
    auto gch = System::Runtime::InteropServices::GCHandle::Alloc(obj);
    auto ip = System::Runtime::InteropServices::GCHandle::ToIntPtr(gch);
    return static_cast<intptr_t>(ip);
}

Now you have an intptr_t that represents a your object and can be freely used with native C++ code. std::map<std::string, intptr_t>

To get your object from the intptr_t:

System::IO::StreamWriter^ ToStreamWriter(intptr_t h) {
    auto gch = System::Runtime::InteropServices::GCHandle::FromIntPtr(static_cast<System::IntPtr>(h));
    return safe_cast<System::IO::StreamWriter^>(gch.Target);
}

The intptr_t value represents a resource the needs to be freed so it should ideally be kept in an RAII object whose destructor will convert it back to a GCHandle (see above) and Free() that. If you don't do that then the StreamWriter object will never be garbage collected and you have a heap leak.

void FreeHandle(intptr_t h) {
    auto gch = System::Runtime::InteropServices::GCHandle::FromIntPtr(static_cast<System::IntPtr>(h));
    gch.Free();
}

I made a whole template library to do this kind of stuff so I can use managed objects freely in native code. I like this kind of thing but I don't necessarily recommend it. I've never seen it done by anyone else, but it does work nicely once you build good tools for it.

Ðаn
  • 10,934
  • 11
  • 59
  • 95
tukra
  • 921
  • 8
  • 13