Wrapper z C++ do C#

0

Hej!
Robię projekt, który rozwiązuje układ równań, dllka jest pisana w C++ a program głowny w C#, niestety korzystam w c++ z dynamicznie alokowanej dwuwymiarowej macierzy oraz z dynamicznie alokowanego wektora...i po stronie C# pojawia się błąd "System.Runtime.InteropServices.MarshalDirectiveException: 'Nie można zorganizować 'return value': Nieprawidłowa kombinacja typów zarządzanych/niezarządzanych."
Wzorując się na poście "https://4programmers.net/Forum/C_i_.NET/184160-wrapper_c++_to_cwywolanie_metody_zwracajacej_tablice_double_w_c" i zamieszczonych tam odpowiedziach myślałam, że program będzie działać, byłabym wdzięczna za sugestię co przegapiłam :)
Z góry wielkie dzięki!
Leks
Po stronie C#:

class Matrix
    {
        private double[,] data;
        private int numberOfRows;

        private int numberOfColumns;
//...dalsze metody/
}

 class Vector
    {
        private double[] data;
        private int numberOfRows;
//dalsze metody
}

public static class DllHelper
    {

        [DllImport(@"...\Elimination.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int[] chooseIdxOfFundamentalElement(int size, int step, double[,] matrixA);
/.......... dalsze funkcje
}

 class MethodSolver
    {
        Matrix matrixA;
        Vector vectorB;
        Vector vectorX;
        int[] changingColumnsInVectorX;

        public MethodSolver(Matrix matrixA, Vector vectorB, Vector vectorX)
        {

            this.matrixA = matrixA;
            this.vectorB = vectorB;
            this.vectorX = vectorX;
            changingColumnsInVectorX = new int[vectorB.NumberOfRows];

        }
        public void solveEquationsUsingElimination()
        {
            int[] idxOfFundamentalElem = new int[2];
            for (int step = 0; step <= matrixA.NumberOfRows; step++)
            {
               
                    idxOfFundamentalElem = DllHelper.chooseIdxOfFundamentalElement(matrixA.NumberOfRows, step, matrixA.Data);
              //......dalsze metody
        }

    }
}


Po stronie C++:

extern "C" ELIMINATION_API int* chooseIdxOfFundamentalElement(int size, int step, double** matrixA);
0

Obiektowe rzeczy z zasady źle się integrują pomiędzy "różnymi światami", np tak, jak u Ciebie.
W DLLce w C++ bym dołożył proste nieobiektowe wrappery

this->AlaMaKota("abc")  => wr_AlaMaKota(class * self, cont char * arg)

Nie mam pojęcia, czy to rozwiąże wszystkie Twoje problemy. Wcale nie będę przysięgał, że jest to najgenialniejszy pomysł, jest tylko "mniej zły" od problemu

0

Spróbuję w takim razie pokombinować, dziękuję za wskazówkę @AnyKtokolwiek, czy zamiast używania np double ** lepszym pomysłem byłoby wykorzystanie STL czy coś mogłoby to zmienić(pomóc)?

0

Dawno się nie bawiłem w interop ale coś mi sie wydaje że może zacznij od deklaracji

'''
[DllImport(@"...\Elimination.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int[] chooseIdxOfFundamentalElement(int size, int step, double[,] matrixA);
'''
Jeżeli po stronie C++ masz int* to zamiast int[] będzie chyba IntPtr.
Później wyniki wyciągasz Marshal.Copy()

Z tymi double** pewnie będzie też jakiś problem, coś mi się obija że trzeba było jakiś [MarshalAs(UnmanagedType.CosTam)] dodać, albo rzucać to jakoś IntPtr. Musiałbym doszukać :-(
Masz kontrolę na interfejsem po stronie C++? Możesz zrobić z tego tablice o znanym rozmiarze?

0

Bardzo Ci dziękuję, jeśli nie pamiętasz to nie ma sprawy poszukam po tym co napisałeś, widziałam właśnie, że stosują niektórzy IntPtr ale myślałam, że może domyślny marshallel załapie o co chodzi. Niestety nie mam, macierze i wektory mają być pobierane już w C# :(

0
Leks napisał(a):

Spróbuję w takim razie pokombinować, dziękuję za wskazówkę @AnyKtokolwiek, czy zamiast używania np double ** lepszym pomysłem byłoby wykorzystanie STL czy coś mogłoby to zmienić(pomóc)?

Akurat na granicy "pomiędzy" nie powinno być ukrytych alokacji, a STL to ma. O ile zwykle to jest dobra rada, to nie tym razem.
Milion lat temu na Borlandzie robiłem (EXE i DLL w tej samej technologii C++), to nawet Stringa należało użyć specjalnego

0
AnyKtokolwiek napisał(a):
Leks napisał(a):

Spróbuję w takim razie pokombinować, dziękuję za wskazówkę @AnyKtokolwiek, czy zamiast używania np double ** lepszym pomysłem byłoby wykorzystanie STL czy coś mogłoby to zmienić(pomóc)?

Akurat na granicy "pomiędzy" nie powinno być ukrytych alokacji, a STL to ma. O ile zwykle to jest dobra rada, to nie tym razem.
Milion lat temu na Borlandzie robiłem (EXE i DLL w tej samej technologii C++), to nawet Stringa należało użyć specjalnego

Dzięki za informację, bo naprawdę się zastanawiałam, że może jednak to rozwiąże problemy, w takim razie będę walczyć z IntPtr i Marshal Copy :)

0

Szkoda, że tego nie można trochę zmienić, teraz masz po stronie C++ wskaźnik do wskaźników, nie pamiętam już jak double[,] będzie reprezentowane przez marshaller ale zagaduję, że to prosto nie będzie, chyba to będziesz musiał jakoś opakować w IntPtr. Taki link wygrzebałem ze swoich ulubionych (więc pewnie kiedyś się z tym ścierałem):
https://social.msdn.microsoft.com/Forums/en-US/d516ec43-52fd-4c64-a414-dff3b1aa3168/how-to-marshalling-a-double-array-between-cc?forum=clr

Przy okazji, znasz juz P/Invoke Interop Assistant
https://github.com/jaredpar/pinvoke-interop-assistant ?

0

Przede wszystkim z dala od STL. C# nie zna STL. Żaden inny język nie zna STL. Co więcej, nawet różne wersje C++ się wywalają. W dll, która ma być dostępna dla innych języków używamy tylko i wyłącznie typów podstawowych. Chyba, że masz jakąś strukturę, czy klasę (bez metod, sam kontener na dane) zdefiniowaną w C++ i chcesz jej użyć. Wtedy po stronie C# też ją musisz analogicznie zadeklarować, pamiętając o offsetach poszczególnych pól. Ewentualnie pamiętając o #pragma pack po stronie C++. A najlepiej to i to :)

Teraz tak. Co do tablic, to nie wiem, bo nigdy tablicami się nie bawiłem.
Natomiast, jeśli nie idą tablice normalnie (a może tak być), to wtedy musisz się posłużyć wskaźnikami. W C++ tablica to tak naprawdę wskaźnik na jej początek.

Czyli możesz dać wskaźnik zamiast tablicy i IntPtr po stronie C#.

A potem analogicznie do tego: https://stackoverflow.com/questions/5486938/c-sharp-how-to-get-byte-from-intptr
Marshal.Copy albo unsafe.

Tablica ZAWSZE jest ciągłym obszarem w pamięci. Nawet wielowymiarowa. Nie wiem tylko, czy C# traktuje tablice kwadratowe tak samo jak C++, czy będziesz musiał kombinować dla każdego "wiersza" osobno.

Możliwe, że po stronie C++ będziesz musiał użyć void * zamiast double *, ale to już musisz sprawdzić sobie sam.

0

Wrapper można napisać w C++/CLI gdzie masz jednocześnie dostęp do pamięci C++, STLa itd. i jednocześnie do .Netowych klas (tych z C#).

0
Azarien napisał(a):

Wrapper można napisać w C++/CLI gdzie masz jednocześnie dostęp do pamięci C++, STLa itd. i jednocześnie do .Netowych klas (tych z C#).

Ale nie możesz .NET stringa przekazać do C++, w praktyce żadnej skomplikowanej struktury nie przekażesz

0

Bardzo Wam wszystkim dziękuję za komentarze! Właśnie do tego siadam i dam znać jeżeli zadziała;na początek będę kombinować z IntPtr i Marshal Copy i zobaczymy jak to wyjdzie, @stic dziękuję za linki, nie znałam wcześniej P/Invoke Interop Assistant więc z pewnością się przyda!

1 użytkowników online, w tym zalogowanych: 0, gości: 1