为什么我不能使用包含 auto 作为参数或 Return 类型的 DLL 中的函数?
Why Can't I Use Functions From a DLL That Contain auto as a Parameter or Return Type?
我正在为我正在创建的编程语言试验动态内存分配。我的主要项目是用 C# 制作的,但我有一个包含创建变量的方法的 C++ DLL。 DLL 方法使用 C# 的 System.Runtime.InteropServices.DllImport()
属性加载。我发现在将我的 C++ 文件构建到 DLL 中(使用 g++ v6.3.0)时,任何 returned auto
或具有 auto
参数的函数都不会导出到 DLL 中。我检查了 dumpbin -exports
,发现它们不包括在内。我设法修复了 DLL,但它让我想知道为什么 auto
类型不会导出。
我知道这不仅仅是C#的问题(我用Python等其他语言测试过,这是在我发现问题是编译问题之前),而且是不仅仅是 g++ 问题,因为其他编译器也无法导出函数。奇怪的是,在这个过程中任何地方都没有抛出错误。
原代码:
// Variable Manipulation methods
extern "C" {
auto CreateVariable(auto value) {
// Create a variable and return its address
// Here, C++ deduces that "value" is any type and the function creates a new object of that type, pointing to the value
return new (typeof(value))(value);
}
auto GetVariable(auto address) {
// Return the value of a variable from the specified address
// Here, C++ deduces that "address" is a pointer of some sort
return *addr;
}
void SetVariable(auto address, auto value) {
// Set the variable using its specified address
// Here, C++ deduces that "address" is a pointer and "value" is any type
*addr = value;
}
void DeleteVariable(auto address) {
// Delete the variable.
// Here, C++ deduces that "address" is a pointer of some sort
delete addr;
}
}
我希望能够使用
[DllImport("dll_path.dll")]
public static extern IntPtr CreateVariable([MarshalAs(UnmanagedType.Any)] object value);
[DllImport("dll_path.dll")]
public static extern object GetVariable(IntPtr address);
[DllImport("dll_path.dll")]
public static extern void SetVariable(IntPtr address, [MarshalAs(UnmanagedType.Any] object value);
[DllImport("dll_path.dll")]
public static extern void DeleteVariable(IntPtr address);
在我的C#程序中,却一直抛出System.EntryPointNotFoundException
,说找不到入口点。自然地,我怀疑 C# 只是对 DLL 很挑剔,但我用 Python 的 ctypes
模块等其他语言进行了测试,它抛出了同样的错误。我确实找到了解决方案:使用 windows.h BYTE
类型 (unsigned char
).
我的问题是:为什么我不能导出带有 auto
参数或 auto
return 类型的函数?
首先,auto
不是 C++ 中的动态类型。 auto
关键字是编译器推断的某种占位符:
// int i1 = 0; // same thing in the eyes of the compiler
auto i1 = 0;
但编译器仅在用值初始化时才推导类型。
// ???? i2;
// i2 = 0;
auto i2; // error! deduce what type?
i2 = 0; // cannot call operator= with unknown type
但是你可以在 lambda 类型中使用 auto
,这有什么不同?
// type of `lambda` is an unnamed type.
auto lambda = [](auto var) { std::cout << var; };
lambda(1);
lambda("string");
即使那看起来是动态的,不是吗。通用 lambda 使用模板实现:
// v----- unnamed
struct {
template<typename T>
auto operator()(auto var) const {
std::cout << var;
}
} lambda{};
这意味着编译器将为auto
参数动态生成新的静态代码。这意味着即使您升级到 C++20,它允许:
auto CreateVariable(auto value) {
// ...
}
实际上不存在任何功能。这只是一个等待实例化的模板。
没有函数可以从您的 dll 中导出,因为它只是一些模板。
你要找的是这样的:
struct CSharpObject;
auto CreateVariable(CSharpObject* value) -> CSharpObject* {
// reflect on value to check the type
// construct a new instance of CSharpObject with the
// right set of metadata for c# to understand
}
不幸的是,C++ 不理解 C# 对象的动态、可反射和垃圾收集,C# 也不理解 C++ 模板和值类型的静态特性。
您需要提供一组对一组已知类型进行操作的函数:
auto CreateVariableInt(int value) -> int* {
return new int{value};
}
auto GetVariableInt(int* address) -> int {
return *addr;
}
auto CreateVariableDouble(double value) -> double* {
return new double{value};
}
auto CreateVariableDouble(double* address) -> double {
return *address;
}
// so on and so forth for every supported types.
然后在 C# 端,保留关于包含什么类型的元数据,并调用正确的函数。
我正在为我正在创建的编程语言试验动态内存分配。我的主要项目是用 C# 制作的,但我有一个包含创建变量的方法的 C++ DLL。 DLL 方法使用 C# 的 System.Runtime.InteropServices.DllImport()
属性加载。我发现在将我的 C++ 文件构建到 DLL 中(使用 g++ v6.3.0)时,任何 returned auto
或具有 auto
参数的函数都不会导出到 DLL 中。我检查了 dumpbin -exports
,发现它们不包括在内。我设法修复了 DLL,但它让我想知道为什么 auto
类型不会导出。
我知道这不仅仅是C#的问题(我用Python等其他语言测试过,这是在我发现问题是编译问题之前),而且是不仅仅是 g++ 问题,因为其他编译器也无法导出函数。奇怪的是,在这个过程中任何地方都没有抛出错误。
原代码:
// Variable Manipulation methods
extern "C" {
auto CreateVariable(auto value) {
// Create a variable and return its address
// Here, C++ deduces that "value" is any type and the function creates a new object of that type, pointing to the value
return new (typeof(value))(value);
}
auto GetVariable(auto address) {
// Return the value of a variable from the specified address
// Here, C++ deduces that "address" is a pointer of some sort
return *addr;
}
void SetVariable(auto address, auto value) {
// Set the variable using its specified address
// Here, C++ deduces that "address" is a pointer and "value" is any type
*addr = value;
}
void DeleteVariable(auto address) {
// Delete the variable.
// Here, C++ deduces that "address" is a pointer of some sort
delete addr;
}
}
我希望能够使用
[DllImport("dll_path.dll")]
public static extern IntPtr CreateVariable([MarshalAs(UnmanagedType.Any)] object value);
[DllImport("dll_path.dll")]
public static extern object GetVariable(IntPtr address);
[DllImport("dll_path.dll")]
public static extern void SetVariable(IntPtr address, [MarshalAs(UnmanagedType.Any] object value);
[DllImport("dll_path.dll")]
public static extern void DeleteVariable(IntPtr address);
在我的C#程序中,却一直抛出System.EntryPointNotFoundException
,说找不到入口点。自然地,我怀疑 C# 只是对 DLL 很挑剔,但我用 Python 的 ctypes
模块等其他语言进行了测试,它抛出了同样的错误。我确实找到了解决方案:使用 windows.h BYTE
类型 (unsigned char
).
我的问题是:为什么我不能导出带有 auto
参数或 auto
return 类型的函数?
首先,auto
不是 C++ 中的动态类型。 auto
关键字是编译器推断的某种占位符:
// int i1 = 0; // same thing in the eyes of the compiler
auto i1 = 0;
但编译器仅在用值初始化时才推导类型。
// ???? i2;
// i2 = 0;
auto i2; // error! deduce what type?
i2 = 0; // cannot call operator= with unknown type
但是你可以在 lambda 类型中使用 auto
,这有什么不同?
// type of `lambda` is an unnamed type.
auto lambda = [](auto var) { std::cout << var; };
lambda(1);
lambda("string");
即使那看起来是动态的,不是吗。通用 lambda 使用模板实现:
// v----- unnamed
struct {
template<typename T>
auto operator()(auto var) const {
std::cout << var;
}
} lambda{};
这意味着编译器将为auto
参数动态生成新的静态代码。这意味着即使您升级到 C++20,它允许:
auto CreateVariable(auto value) {
// ...
}
实际上不存在任何功能。这只是一个等待实例化的模板。
没有函数可以从您的 dll 中导出,因为它只是一些模板。
你要找的是这样的:
struct CSharpObject;
auto CreateVariable(CSharpObject* value) -> CSharpObject* {
// reflect on value to check the type
// construct a new instance of CSharpObject with the
// right set of metadata for c# to understand
}
不幸的是,C++ 不理解 C# 对象的动态、可反射和垃圾收集,C# 也不理解 C++ 模板和值类型的静态特性。
您需要提供一组对一组已知类型进行操作的函数:
auto CreateVariableInt(int value) -> int* {
return new int{value};
}
auto GetVariableInt(int* address) -> int {
return *addr;
}
auto CreateVariableDouble(double value) -> double* {
return new double{value};
}
auto CreateVariableDouble(double* address) -> double {
return *address;
}
// so on and so forth for every supported types.
然后在 C# 端,保留关于包含什么类型的元数据,并调用正确的函数。