我如何将 map<string, int> 从 C++ 获取到 C#
How I can get map<string, int> from C++ to C#
我正在尝试从 dll c++ 获取地图
所以我必须获取地图并将其解析为 C# 端的字典。
我已尝试执行以下步骤,但没有成功。
C++ 代码:
extern "C" __declspec(dllexport) map<string, int> createMap(string &fileName) {
ifstream infile(fileName);
vector<string> bitsLine;
bool headerEnded = false;
string line;
int i = 0;
int length = 0;
while (getline(infile, line)) {
if (headerEnded) {
bitsLine = split(line, ',');
signalsMap.insert({ bitsLine.at(0), length });
}
else {
if (line.find("HEADER_END") != std::string::npos) {
headerEnded = true;
}
}
length = infile.tellg();
i++;
}
return signalsMap;
}
C#代码:
Dictionary<string, int> x = createMap("C:/users/asalah/source/repos/WindowsFormsApp3/WindowsFormsApp3/RR_Test2_3.csv");
不幸的是,这个问题的简单答案是 "you shouldn't"。您不应该首先从 dll 中导出 STL 类型,更不用说尝试在 C# 中封送它们了。 STL 类型的内存布局可能因编译器而异,C++ 运行时与 C++ 运行时不同。它可能会导致非常脆弱的代码。因此,如果您导出 C 函数,它应该采用 const char*
而不是 std::string
例如。
您可以做的是在每个键和值可用时编组它们。这样做的好处是您不必对内存管理做任何工作,并且集成到您已有的东西中相当简单,尽管我没有对性能发表任何声明。
这里有一个简短的 C++ 和 C# 示例,如果对您有帮助的话,可以帮助您继续进行此类解决方案:
extern "C" __declspec(dllexport) void doFoo(void(*adder)(const char*, int32_t))
{
adder("Test", 346);
}
下面是使用此 API 的 C# 代码。它应该只是将 "Test" 的值为 346 添加到字典中,仅此而已。它通过调用一个回调函数来做到这一点,该回调函数是 Dictionary.Add 周围的本机垫片,用于字典的指定实例。
namespace Eff3
{
using System.Collections.Generic;
using System.Runtime.InteropServices;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void DictionaryAdd(string key, int value);
class Program
{
[DllImport("TestDll", CallingConvention = CallingConvention.Cdecl)]
static extern void doFoo(DictionaryAdd callback);
static void Main()
{
var result = new Dictionary<string, int>();
doFoo(result.Add);
}
}
}
我已经在我的机器上测试过了,我在 x64 的 Visual C++ 2017 中构建了 DLL,并在 C# 中禁用了 "Prefer 32-bit"。
我正在尝试从 dll c++ 获取地图 所以我必须获取地图并将其解析为 C# 端的字典。 我已尝试执行以下步骤,但没有成功。
C++ 代码:
extern "C" __declspec(dllexport) map<string, int> createMap(string &fileName) {
ifstream infile(fileName);
vector<string> bitsLine;
bool headerEnded = false;
string line;
int i = 0;
int length = 0;
while (getline(infile, line)) {
if (headerEnded) {
bitsLine = split(line, ',');
signalsMap.insert({ bitsLine.at(0), length });
}
else {
if (line.find("HEADER_END") != std::string::npos) {
headerEnded = true;
}
}
length = infile.tellg();
i++;
}
return signalsMap;
}
C#代码:
Dictionary<string, int> x = createMap("C:/users/asalah/source/repos/WindowsFormsApp3/WindowsFormsApp3/RR_Test2_3.csv");
不幸的是,这个问题的简单答案是 "you shouldn't"。您不应该首先从 dll 中导出 STL 类型,更不用说尝试在 C# 中封送它们了。 STL 类型的内存布局可能因编译器而异,C++ 运行时与 C++ 运行时不同。它可能会导致非常脆弱的代码。因此,如果您导出 C 函数,它应该采用 const char*
而不是 std::string
例如。
您可以做的是在每个键和值可用时编组它们。这样做的好处是您不必对内存管理做任何工作,并且集成到您已有的东西中相当简单,尽管我没有对性能发表任何声明。
这里有一个简短的 C++ 和 C# 示例,如果对您有帮助的话,可以帮助您继续进行此类解决方案:
extern "C" __declspec(dllexport) void doFoo(void(*adder)(const char*, int32_t))
{
adder("Test", 346);
}
下面是使用此 API 的 C# 代码。它应该只是将 "Test" 的值为 346 添加到字典中,仅此而已。它通过调用一个回调函数来做到这一点,该回调函数是 Dictionary.Add 周围的本机垫片,用于字典的指定实例。
namespace Eff3
{
using System.Collections.Generic;
using System.Runtime.InteropServices;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void DictionaryAdd(string key, int value);
class Program
{
[DllImport("TestDll", CallingConvention = CallingConvention.Cdecl)]
static extern void doFoo(DictionaryAdd callback);
static void Main()
{
var result = new Dictionary<string, int>();
doFoo(result.Add);
}
}
}
我已经在我的机器上测试过了,我在 x64 的 Visual C++ 2017 中构建了 DLL,并在 C# 中禁用了 "Prefer 32-bit"。