Wrapper z C++ do C#

Odpowiedz Nowy wątek
2019-11-17 18:44

Rejestracja: 2 lata temu

Ostatnio: 1 miesiąc temu

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/Ci.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);
edytowany 4x, ostatnio: Leks, 2019-11-17 18:46

Pozostało 580 znaków

2019-11-17 19:19

Rejestracja: 1 rok temu

Ostatnio: 20 minut temu

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

edytowany 1x, ostatnio: AnyKtokolwiek, 2019-11-17 19:21

Pozostało 580 znaków

2019-11-17 20:40

Rejestracja: 2 lata temu

Ostatnio: 1 miesiąc temu

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)?

Pozostało 580 znaków

2019-11-17 21:12

Rejestracja: 9 miesięcy temu

Ostatnio: 4 dni temu

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?

Pozostało 580 znaków

2019-11-17 21:19

Rejestracja: 2 lata temu

Ostatnio: 1 miesiąc temu

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# :(

Wydaje mi się, że będziesz potrzebował metod generycznych z C# do macierzy i wektorów, bo to źle ze sobą współgra. A właściwie w ogóle nie współgra. - GironX 2019-11-18 14:22
Spróbuję różne kombinacje na podstawie komentarzy i dam znać co przeszło, dziękuję za informację :) - Leks 2019-11-19 12:46

Pozostało 580 znaków

2019-11-17 21:26

Rejestracja: 1 rok temu

Ostatnio: 20 minut temu

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

Pozostało 580 znaków

2019-11-17 21:31

Rejestracja: 2 lata temu

Ostatnio: 1 miesiąc temu

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 :)

Pozostało 580 znaków

2019-11-17 23:04

Rejestracja: 9 miesięcy temu

Ostatnio: 4 dni temu

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[...]le-array-between-cc?forum=clr

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

Pozostało 580 znaków

2019-11-18 11:16

Rejestracja: 17 lat temu

Ostatnio: 2 dni temu

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/que[...]p-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.

edytowany 1x, ostatnio: Juhas, 2019-11-18 11:20

Pozostało 580 znaków

2019-11-18 18:28

Rejestracja: 16 lat temu

Ostatnio: 6 godzin temu

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#).

Pozostało 580 znaków

2019-11-18 18:37

Rejestracja: 1 rok temu

Ostatnio: 20 minut temu

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

Możesz, bo C# robi tutaj całą magię. - Juhas 2019-11-19 10:53

Pozostało 580 znaków

Odpowiedz

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