locked
Returning a Vector of Vectors from CLI Wrapper to C++ RRS feed

  • Question

  • I am trying to return a vector from Managed CLI/C++ to Unmanaged C++, it throws the below

    BrownianBridgeAdapter {
    public:array<double, 3>^ CallingBrownianManagedCodeTemp()
    {
               int numPaths = 100;
               int dimension = 3;
               double time = 30;
               int seed = 1;
    
               double stepSize = 1.0 / 12.0;
               int scrambling = 0;
    
               array<double, 2>^ covMatrix = { { 1.0, 0.0, 0.0 },
               { 0.0, 1.0, 0.0 },
               { 0.0, 0.0, 1.0 } };
    
               std::string str = "Sobol";
               String^ newSystemString = gcnew String(str.c_str());
    
               array<double, 3>^ result = (array<double, 3>^) IAHalley::RandomNumberGenerator::iBrownianBridge(numPaths, dimension, time, seed, covMatrix, stepSize, newSystemString, scrambling);
    
               return result;
    }
    };
    

    Export Definition:

    __declspec(dllexport) vector<vector<vector<double>>> CallBrownianManagedAdapter()
     {
        vector<vector<vector<double>>> myVector;
        BrownianWrapper::BrownianBridgeAdapter objectBrownian;
        array<double, 3>^ brownianArray = objectBrownian.CallingBrownianManagedCodeTemp();
        int n1 = brownianArray->GetLength(0);
        int n2 = brownianArray->GetLength(1);
        int n3 = brownianArray->GetLength(2);
    
    for (int i = 0; i < n1; i++)
    {
        vector<vector <double>> vector1;
        myVector.push_back(vector1);
        for (int j = 0; j < n2; j++)
        {
            vector <double> vector2;
            myVector[i].push_back(vector2);
            for (int k = 0; k < n3; k++)
            {
                myVector[i][j].push_back(rand());
            }
        }
    }
    
    delete(brownianArray);
    return myVector;
    }

    Calling Program:

    #include "stdafx.h"
    #include "conio.h"
    #include <iostream>
    #include <windows.h>
    #include "BrownianWrapper.h"
    #include <vector>
    
    #define new DEBUG_NEW
    
    _declspec(dllexport) std::vector<std::vector<std::vector<double>>> CallBrownianManagedAdapter();
    
    int _tmain()
    {
     auto myVector = CallBrownianManagedAdapter();
     return 0;
    }

    errors.

    First-chance exception at 0x74FAC52F in CallingMangedMethodsDemo.exe:  Microsoft C++ exception: EEFileLoadException at memory location 0x003ED554.
    First-chance exception at 0x74FAC52F in CallingMangedMethodsDemo.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
    First-chance exception at 0x74FAC52F in CallingMangedMethodsDemo.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
    First-chance exception at 0x74FAC52F in CallingMangedMethodsDemo.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
    First-chance exception at 0x74FAC52F in CallingMangedMethodsDemo.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
    First-chance exception at 0x74FAC52F (KernelBase.dll) in CallingMangedMethodsDemo.exe: 0xE0434352 (parameters: 0x8007007E, 0x00000000, 0x00000000, 0x00000000, 0x6EE20000).
    First-chance exception at 0x74FAC52F in CallingMangedMethodsDemo.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000.
    Unhandled exception at 0x74FAC52F (KernelBase.dll) in CallingMangedMethodsDemo.exe: 0xE0434352 

    It is throwing various errors; I was able to successfully call the method using a Managed C++ Console APP. It throws the below errors. I've copied all related dlls into same folder. Any help would be appreciated.

    Thanks

    Dev

    Monday, April 18, 2016 6:29 PM

Answers

  • Hi David,

    I changed my declaration as below

    __declspec(dllexport) void CallBrownianWrapper(double* myArray)
    {
        BrownianWrapper::BrownianBridgeAdapter objectBrownian;
        array<double, 3>^ brownianArray = objectBrownian.CallingBrownianManagedCodeTemp();
    
        int n1 = brownianArray->GetLength(0);
        int n2 = brownianArray->GetLength(1);
        int n3 = brownianArray->GetLength(2);
    
        for (int i = 0; i < n1; i++)
        {
            for (int j = 0; j < n2; j++)
            {
                for (int k = 0; k < n3; k++)
                {
                myArray[(i*n2 + j)*n3 + k] = brownianArray[i, j, k];                
                }
            }
        }
    
    }

    Import Declaration as

    _declspec(dllimport) void CallBrownianWrapper(double* myArray);

    Can you please guide me on how to call and read the elements from the array?

    Thanks

    Well, first, as I explained in my last post, you need to have a method for getting n1, n2 and n3 in the client code. Then you can do

    double* myArray = new double[n1*n2*n3];
    CallBrownianWrapper(myArray);
    // use myArray
    delete [] myArray;

    The i,j,k element of the array is obtained by

    double xijk = myArray[(i*n2 + j)*n3 + k];

    David Wilkinson | Visual C++ MVP


    • Edited by davewilk Tuesday, April 19, 2016 9:55 PM
    • Proposed as answer by Hart Wang Wednesday, April 27, 2016 9:12 AM
    • Marked as answer by Hart Wang Friday, April 29, 2016 9:11 AM
    Tuesday, April 19, 2016 9:54 PM

All replies

  • 1. It is not generally a good idea to pass native library objects between different modules. If the modules are not compiled with exactly the same versions of the compiler, and exactly the same settings, then mysterious run time errors may occur.

    Why did you not like the method I showed you earlier that just passed a simple array (of the correct size)? You could get the sizes with another exported function like

    __declspec(dllexport) int CallBrownianManagedAdapterLength(int i) { BrownianWrapper::BrownianBridgeAdapter objectBrownian; array<double, 3>^ brownianArray = objectBrownian.CallingBrownianManagedCodeTemp(); return brownianArray->GetLength(i);
    }

    2. The client code that uses a function exported from a DLL should use __declspec(dllimport) on the declaration.

    3. I don't think you should be deleting brownianArray in your exported function.


    David Wilkinson | Visual C++ MVP

    • Proposed as answer by Hart Wang Tuesday, April 19, 2016 5:50 AM
    Monday, April 18, 2016 7:51 PM
  • Hi David,

    I tried your method, but it was giving strange errors, so I thought copying the contents to a vector is much easier. As I am new to C++ it was quite painful to figure out the errors.

    However I will give another try.

    So, I am assuming It is not a good idea to return a vector from CLI to unmanaged code. I will update you how it goes.

    Thanks

    Dev 

    Tuesday, April 19, 2016 7:02 PM
  • Hi David,

    I changed my declaration as below

    __declspec(dllexport) void CallBrownianWrapper(double* myArray)
    {
    	BrownianWrapper::BrownianBridgeAdapter objectBrownian;
    	array<double, 3>^ brownianArray = objectBrownian.CallingBrownianManagedCodeTemp();
    
    	int n1 = brownianArray->GetLength(0);
    	int n2 = brownianArray->GetLength(1);
    	int n3 = brownianArray->GetLength(2);
    
    	for (int i = 0; i < n1; i++)
    	{
    		for (int j = 0; j < n2; j++)
    		{
    			for (int k = 0; k < n3; k++)
    			{
    			myArray[(i*n2 + j)*n3 + k] = brownianArray[i, j, k];				
    			}
    		}
    	}
    
    }

    Import Declaration as

    _declspec(dllimport) void CallBrownianWrapper(double* myArray);

    Can you please guide me on how to call and read the elements from the array?

    Thanks

    Tuesday, April 19, 2016 7:28 PM
  • Hi David,

    I changed my declaration as below

    __declspec(dllexport) void CallBrownianWrapper(double* myArray)
    {
        BrownianWrapper::BrownianBridgeAdapter objectBrownian;
        array<double, 3>^ brownianArray = objectBrownian.CallingBrownianManagedCodeTemp();
    
        int n1 = brownianArray->GetLength(0);
        int n2 = brownianArray->GetLength(1);
        int n3 = brownianArray->GetLength(2);
    
        for (int i = 0; i < n1; i++)
        {
            for (int j = 0; j < n2; j++)
            {
                for (int k = 0; k < n3; k++)
                {
                myArray[(i*n2 + j)*n3 + k] = brownianArray[i, j, k];                
                }
            }
        }
    
    }

    Import Declaration as

    _declspec(dllimport) void CallBrownianWrapper(double* myArray);

    Can you please guide me on how to call and read the elements from the array?

    Thanks

    Well, first, as I explained in my last post, you need to have a method for getting n1, n2 and n3 in the client code. Then you can do

    double* myArray = new double[n1*n2*n3];
    CallBrownianWrapper(myArray);
    // use myArray
    delete [] myArray;

    The i,j,k element of the array is obtained by

    double xijk = myArray[(i*n2 + j)*n3 + k];

    David Wilkinson | Visual C++ MVP


    • Edited by davewilk Tuesday, April 19, 2016 9:55 PM
    • Proposed as answer by Hart Wang Wednesday, April 27, 2016 9:12 AM
    • Marked as answer by Hart Wang Friday, April 29, 2016 9:11 AM
    Tuesday, April 19, 2016 9:54 PM