|
-
October 22nd, 2014, 04:44 PM
#1
How to access a managed 2D array in C++ *without* making a deep copy?
Hello.
What is the recommended way to access a managed 2D on the C++ side without having to make a deep copy?
Scenario is as follows: The C++/CLI function that will be called from the C# side has a signature of foo(array<double,2> A) and the native C function I need to call is bar(double **ptr).
With a 1D array things are straightforward with pin_ptr<T> magic:
Code:
void bar(double *ptr);
void foo(array<double> ^A)
{
pin_ptr<double> pinned = &A[0];
double *ptr = pinned;
bar(ptr);
}
But as soon as I have a 2D array, I have a problem with pin_ptr<T>, because it will go out of scope too early:
Code:
void bar(double **ptr);
void foo(array<double,2> ^A)
{
const int len = A->GetLength(0);
double **ptr = (double**) alloca(sizeof(double*) * len);
for(int i = 0; i < len; i++)
{
pin_ptr<double> pinned = &A[i,0]; //Will go out of scope at end of loop body!
ptr[i] = pinned;
}
bar(ptr); //Not save <-- pinned pointers have been released already :-(
}
So what is the recommended way to go here?
I cannot retain my pin_ptr<T>'s, since VS says I cannot make an array (neither managed nor unmanaged) of this type. Nor can it be in a STL container 
------------------
After some digging, I found out that GCHandle::Alloc(x, GCHandleType::Pinned) may work as a more flexible replacement for pin_ptr<T> here. However, it seems we can only pin down the managed array as a whole. It does not seem to be possible to pin down a single sub-array (inner array) this way, like the pin_ptr<T> would do. Furthermore, by "try and error" method I have figured out that from the GCHandle I can get an unmanaged pointer via h.AddrOfPinnedObject().ToPointer() and that this one points to a continuous block of memory which contains the whole 2D array in a "flattened" (serialized) form. From here I can reconstruct the unmanaged 2D array, by using the proper base-pointer and stride. But is this considered a "safe" method and does it always work or is it even implementation specific? 
What I currently use looks like this:
Code:
class ArrayPinHandlerRAII
{
public:
ArrayPinHandlerRAII(array<double,2> ^managedArray)
{
m_dimOuter = managedArray->GetLength(0);
m_dimInner = managedArray->GetLength(1);
m_handle = GCHandle::Alloc(managedArray, GCHandleType::Pinned);
m_ptr = new double*[m_dimOuter];
double *basePointer = reinterpret_cast<double*>
(m_handle.AddrOfPinnedObject().ToPointer());
for(size_t d = 0; d < m_dimOuter; d++)
{
m_ptr[d] = basePointer;
basePointer += m_dimInner;
}
}
~ArrayPinHandlerRAII(void)
{
delete [] m_ptr;
m_handle.Free();
}
inline double **data(void)
{
return m_ptr;
}
inline const size_t &dimOuter(void) const
{
return m_dimOuter;
}
inline const size_t &dimInner(void) const
{
return m_dimInner;
}
private:
GCHandle m_handle;
double **m_ptr;
size_t m_dimOuter;
size_t m_dimInner;
};
Code:
void bar(double **ptr);
void foo(array<double,2> ^A)
{
ArrayPinHandlerRAII myPinnedArray(A)
bar(myPinnedArray.data());
}
What do you think about this method? 
Regards.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|