首页 > 解决方案 > 为什么我不能使用包含 auto 作为参数或返回类型的 DLL 中的函数?

问题描述

我正在为我正在创建的编程语言尝试动态内存分配。我的主要项目是用 C# 制作的,但我有一个 C++ DLL,其中包含创建变量的方法。DLL 方法使用 C# 的System.Runtime.InteropServices.DllImport()属性加载。我发现在将我的 C++ 文件构建到 DLL 中(使用 g++ v6.3.0)时,任何返回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.hBYTE类型 ( unsigned char)。

我的问题是:为什么我不能导出带有auto参数或auto返回类型的函数?

标签: c#c++dllauto

解决方案


首先,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

但是你可以有auto一个 lambda 类型,有什么不同呢?

// 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# 端,保留有关包含什么类型的元数据,并调用正确的函数。


推荐阅读