Gọi vector<struct> của C++ ở C#

Chào mọi người.
Mình khá là newbie trong mảng C++, nay có vấn đề này vẫn chưa biết làm sao cho tốt, đã tham khảo ở các diễn đàn khác nhưng thông tin khá cũ nên mình post lên đây nhờ mọi người giải đáp giúp.
Chuyện là ở C++ Dll của mình có 1 struct như sau:

struct PointXYZ
{ 
float x,
float y,
float z,
};

mình có function truyền vào vector các struct này theo dạng tham chiếu (vì số lượng phần tử k cố định nên mình không dùng mảng được-theo mình hiểu là vậy)

Err FunctionName(vector<PointXYZ> &points) {//add phần tử cho vector}

Vấn đề: Làm sao để ở code C# mình có thể dùng hàm này, kiểu dữ liệu tương ứng cho vector này là như thế nào.

P/s: mình có tìm hiểu qua các vấn đề liên quan đến COM, P/Invoke, C++/CLI , Marshal nhưng thật sự là còn rất mơ hồ nên vẫn chưa biết bước tiếp theo nên làm thế nào.

Mong nhận được sự giúp đỡ, gợi ý từ mọi người.

1 Like

Cái mà bạn cần là Marshal. .NET không tương thích với std::vector<> (khác layout bên dưới), nên bạn sẽ phải sử dụng data types mà nó có thể nuốt được.

DLL:

#define API __declspec(dllexport)

struct Point { float x, y, z; };

extern "C" {
  API __cdecl void Process(Point points[], int n)
  {
    for (int i = 0; i < n; ++i) {
      std::printf("{%.2f; %.2f; %.2f}\n", points[i].x, points[i].y, points[i].z);
    }
  }
}

Vì dùng array không biết length của mảng, nên cần thêm argument size để C++ biết cần xử lý bao nhiêu element.

.NET:

struct Point
{
    public float x, y, z;
    public Point(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

...

Point[] points = {
    new Point(1.0f, 2.0f, 3.0f),
    new Point(4.0f, 5.0f, 6.0f),
    new Point(7.0f, 8.0f, 9.0f)
};

Process(points, points.Length);
4 Likes

Cám ơn câu trả lời của bạn.
Hình như cách đặt vấn đề của mình đang bị ngược hoặc hơi khó hiểu.

  1. Ở C++ thì vertor đó đang truyền kiểm tham biến, mình muốn hàm đó ghi dữ liệu vào cho vector rỗng mà mình truyền vào.
  2. Số lượng phần tử của vector là không cố định, tùy theo đầu vào.
  3. Từ C# mình sẽ gửi cái gì đó tương ứng với vector ở C++ để có thể lấy được dữ liệu.

Mình đã hiểu vấn đề là C# không có kiểu dữ liệu nào tương ứng với vector. Nên bạn có thể gợi ý giải pháp nào khác cho bài toán của mình không?

Khi không có kiểu dữ liệu tương đồng, tức là cấu trúc dữ liệu ở 2 ngôn ngữ là khác nhau thì không có cái cách nào để chuyển trực tiếp như vậy.

Để làm được chỉ có cách làm cho cấu trúc dữ liệu của nó giống nhau.
Đầu tiên cấu trúc cái struct ở 2 ngôn ngữ phải giống hệt nhau.
Tiếp đến cái mảng (vector) cũng có cấu trúc giống hệt nhau. Bên C++ có thể truyền con trỏ qua. Bên C# có thể dùng mảng, IntPtr để hứng. Tất nhiên có kèm theo kích thước dữ liệu.

3 Likes

Cám ơn câu trả lời của bạn.
Mình đã hiểu là không thể truyền trực tiếp vì kiểu dữ liệu khác nhau.
Tuy nhiên câu trả lời của bạn có 2 điểm mình chưa rõ:

  1. Mình truyền kiểu tham chiếu vì muốn thay đổi số lượng phần tử của nó nên mới dùng vector. Nếu truyền con trỏ thì sẽ truyền thế nào.
  2. Như đã nói ở trên là kích thước dữ liệu không cố định do tùy vào điều kiện đầu vào, nên nếu không truyền được kích thước thì mình phải làm thế nào.
    Thân!

Tại sao không thể pass size nhỉ? Với vectors/arrays/lists/… sẽ luôn biết được size mà, thêm/bớt element thì size cũng thay đổi theo.

Pass bằng reference thì:

Process(Point (&points)[], ...);

Ở C#, pass array bằng ref.

3 Likes

ở bên trong function ở C++, tùy vào điều kiện khác nhau mà số lượng phần tử được add thêm vào nó sẽ khác nhau, nên mình k biết points.Length để truyền vào được. Mình đang băn khoăn ở chổ đó.

Tức là bạn muốn pass cái vector qua C# rồi bên C# có thể thay đổi số phần tử ?
Nếu vậy thì không có mùa xuân ấy ::))
Bởi vì ngay như đầu đã nói kiểu dữ liệu không tương đồng thì C# nó sẽ không hiểu vector là cái gì, push_back, erase… là gì và hoạt động như thế nào.
Bạn chỉ có thể pass con trỏ qua kèm kích thước. Bên C# sẽ tạo một mảng mới pass ngược lại kèm kích thước mới.
Hoặc tự xây dựng một cấu trúc dữ liệu giống vector và tương đồng cả 2 bên.

4 Likes

Ý của mình là từ C# mình sẽ pass cái gì đó tương ứng với vector trong C++, việc thay đổi số phần tử xảy ra bên trong hàm ở C++ bạn ơi.

Gần giống vector thì bên C# có List. Nhưng cod tương đồng với vector không thì không chắc.
Nhưng nếu đã không muốn thay đổi số phần tử ở ngôn ngữ bên C# thì pass cái con trỏ kèm số lượng phần tử là xong còn khúc mắc gì nữa đâu ?

1 Like

ở bên trong function ở C++, tùy vào điều kiện khác nhau mà số lượng phần tử được add thêm vào nó sẽ khác nhau, số lượng phần tử này mình không biết trước được chứ không phải không muốn thay đổi đâu bạn.

Vậy khó à.
Bằng cách này/cách khác bạn vẫn phải encode length hoặc NULL byte ở cuối để DLL biết lúc nào cần dừng đọc.
Mình không rành .NET nên không rõ có cách nào lấy pointer hay allocate array bằng tay (unsafe?), nếu allocate bằng tay được, bạn có thể thêm byte magic ở cuối list, rồi pass vào C++.
C++ đọc pointer, nếu đọc được magic byte torng list của bạn thì dừng.

3 Likes

Bạn nói vô lý.
Làm sao mà không biết số phần tử ?
Cấp phát động thì đều phải có thông tin kích thước khi cấp phát.
vector thì đã có size, string đã có length.
Đơn luồng thì chả có cái gì thay đổi khi đang xử lý.
Đa luồng thì cũng có mutex, lock để khoá không cho dữ liệu thay đổi.
Cấp phát bộ nhớ, sử dụng dữ liệu mà không kiểm soát nổi nó như thế nào có bằng giết nhau :neutral_face:

3 Likes

Những cái bạn vừa nói đều có lý cả, nhưng bài toán của mình đang nói là vấn đề khác, bạn có thể tham khảo thread comment của mình và Florastamine để tham khảo :smiley:

Cách đơn giản nhất là convert sang json rồi truyền string qua C#, từ json C# convert lại struct cần. Cách này giảm hiệu năng chút nhưng đơn giản dễ thực hiện.

2 Likes

Cám ơn bạn, có thời gian mình sẽ thử thêm cách này.
Mình đang cần ưu tiên hiệu năng, vì ứng dụng này chạy realtime, nên có cách nào khác không bạn. Mình không nhất thiết phải dùng vector hay gì đó, cái mình cần chỉ là có thể thêm vào dữ liệu một cách không cố định số lượng thôi.

Chuyển qua chuyển lại struct nữa C++ C# hay bất kỳ ngôn ngữ nào tương đối phức tạp và đến giờ phút này m vẫn chưa tìm thấy cái nào thực sự chạy đc ngon.
Chỉ truyền qua lại đc bytearray hoặc string qua lại chứ mấy struct phức tạp thì bó tay, còn ko chỉ truyền đc pointer C++ cho C# “giữ dùm” chứ ko chuyển toàn bộ dữ liệu của C++ qua C# trực tiếp đc.
Mình đang sài 2 cách sau để xử lý vấn đề tương đối giống của bạn (m code c++ cho cả android và ios bất chấp kiểu dữ liệu muốn chuyển 2 cách này luôn sài đc):

  • Đơn giản thì bạn có thể convert struct sang json để chuyển như string.
  • Còn tăng hiệu năng chút thì bạn có thể sài Flatbuffers để chuyển qua lại như là 1 bytearray.
3 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?