【问题标题】:Take an array<double>^ and return an array<Complex>^ pointing to the same memory取一个数组<double>^ 并返回一个指向同一内存的数组<Complex>^
【发布时间】:2016-03-05 23:34:51
【问题描述】:

我有一个 C++ 函数,它接受一个数组 (double*) 作为参数,对数据进行处理并返回一个指向相同数据的 complex* 类型的指针。我需要使用 c++/cli 包装这个函数,但我不确定如何确保 .NET 最终得到对内存的正确引用。例如,如果用户像 Complex[] data = Process(new double[1024]); 这样调用函数,我想确保在函数返回时数据不会被垃圾收集(并且我的 pin_ptr 超出范围)并且它确实在data 超出范围。 我怎样才能得到两个对内存的有效引用,每个引用都不同? 注意:底层 C++ 函数工作正常,这是一个 .NET C++/CLI 问题,关于如何获取对同一内存的两个垃圾收集引用,两种不同类型。

#pragma unmanaged
complex<double>* NativeFft(double* real_time_data, int data_size) {
    // do in-place FFT on data (no allocations)
    return reinterpret_cast<complex<double*>>(real_time_data);
}

#pragma managed
array<Complex>^ Transform::Fft(array<double>^ real_time_data) {
    if (real_time_data== nullptr) {
        throw gcnew ArgumentNullException("'real_time_data' must not be null");
    }
    int data_size = real_time_data->Length;
    pin_ptr<double> data_ptr = &real_time_data[0];
    complex<double>* complex_freq_data = NativeFft(data_ptr, data_size);
    // now how do I make another reference to the data?
    // (I also need the Length field to be halved).
    array<Complex>^ managed_complex_freq_data = ???
    return managed_complex_freq_data;
}

编辑: 这是更多信息,因为每个人都说明显的解决方案是复制数据。 我正在做的是一个非常大的从实到复杂的原位 FFT。数据以双精度数组的形式出现,并且基本上保持这种状态(显然值会发生变化),但是在 FFT 完成后,对数据的正确解释是每对双精度都构成单精度的实部和虚部复杂的样本。我想做的 FFT 的大小对于 32 位系统来说太大了,但我不想尽可能接近它,所以我不想牺牲做副本所需的内存数据。我可以始终将数据作为双精度来处理,但能够将其视为复杂的会很好,因为这就是 FFT 之后的真实情况。

【问题讨论】:

  • 它是托管类型 (^),所以您不要这样做。请阅读What is the XY problem? 并相应地更改您的问题。
  • new double[1024] 应该在函数返回后收集垃圾,因为它不再需要。返回值应该从data_out 构建(在调用NativeProcess 之后,您已经完成了data,所以不知道为什么您认为需要使用相同的“内存”)。
  • 我需要使用相同的内存,因为数组会尽可能大,即使原始数组被销毁,我也没有空间将它复制到另一个数组,因为对于片刻之后,两个数组都将存在,我将耗尽内存。

标签: arrays casting c++-cli


【解决方案1】:

我可能完全误解了您的问题或您的要求,但是将本机数组的内容复制到托管数组中并让 GC 完成它的工作不是更简单吗?

#include <complex>

using namespace System;
using namespace System::Numerics;

std::complex<double>* NativeProcess(double* data, int& size);

public ref class Processor
{
public:
    array<Complex>^ Processor::Process(array<double>^ data)
    {
        if (data == nullptr)
            throw gcnew ArgumentNullException("data");

        int length = data->Length;
        pin_ptr<double> dataPtr = &data[0];
        std::complex<double>* dataOut = NativeProcess(dataPtr, length);

        array<Complex>^ complexArray = gcnew array<Complex>(length);
        for (int idx = 0; idx < length; ++idx)
            complexArray[idx] = Complex(dataOut[idx].real(), dataOut[idx].imag());

        delete[] dataOut;
        return complexArray;
    }
};

【讨论】:

  • 对于 64 位版本的软件复制将起作用。对于 32 位版本,没有那么多。基本上它是完全受内存限制的,因此复制会将我可以使用的数组大小减半。
【解决方案2】:

我不确定从 double 到 Complex 的转换。是否可以在不创建新对象的情况下将基本类型的 double 转换为托管类型的 Complex?如果您需要更多详细信息,我可以寻找它。

但无论如何:在任何指针指向它之前,不会收集您的数据。

【讨论】:

  • 理论上有可能,实际上我不知道。在我的 C++/CLI 代码中,我获取一个数组^,将其固定,然后将固定值转换为原生 complex*。所以托管到本地(反之亦然)不是问题。在 C# 中,很容易对现有数组进行新引用(只需分配它。)但我不知道如何将这两个操作结合起来,在 C++/CLI 中创建一个不同类型的新引用.关于你的第二点,有效的 gc 指针是正确的,本机指针不是这样。如果我可以创建一个指向数据的有效 gc 指针,我同意。
  • 我从问题中了解到,您需要更多“不丢失”指针,什么转换数据。为什么我说“不确定”。关于代码中的第二点,我们看到 array**^** 和 array**^**,所以 gc 应该可以工作。
猜你喜欢
  • 1970-01-01
  • 2012-08-18
  • 1970-01-01
  • 2020-09-16
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-12
相关资源
最近更新 更多