为啥我们不能将本地 C++ 的引用传递给 C++/CLI?

Posted

技术标签:

【中文标题】为啥我们不能将本地 C++ 的引用传递给 C++/CLI?【英文标题】:Why can't we pass a reference from native C++ to C++/CLI?为什么我们不能将本地 C++ 的引用传递给 C++/CLI? 【发布时间】:2014-07-29 07:58:26 【问题描述】:

我使用来自 C# 的本机 C++ 代码库,以及围绕它构建的 C++/CLI 包装器(使用 Visual Studio 2013)。有两个项目:

NativeCodeBase:简单的 C++ 项目集构建到静态库中。 ManagedWrapper:C++/CLI 项目引用 NativeCodeBase。

我在 NativeCodeBase 中有以下原生“接口”:

class ITest 
    virtual void Foo(const std::string& str, MyEnum me) = 0;

为此,我在 ManagedWrapper 项目中有一个本机实现。 在标题中:

class TestManaged : public ITest 
    virtual void Foo(const std::string& str, MyEnum me) override;

在cpp中:

void TestManaged::Foo(const std::string& str, MyEnum me) 
    int length = str.length();

MyEnum 枚举在本机代码和托管代码中都使用,因此在其实现中,我使用条件编译的 C++/CLI 扩展,使其可在 C# 中使用:

#ifdef _MANAGED
    public
#endif
    enum class MyEnum : unsigned char
    
        Baz = 0,
        Qux = 1
    ;

在我的本机代码中,我引用了 ITest 并使用本地 std::string 变量调用其 Foo 函数。当调用 Foo 时,我可以在调试器中看到作为参数传递的字符串是一个有效的字符串对象。

调用类似这样:

void Bar(ITest& test) 
    std::string str = "test";
    test.Foo(str, MyEnum::Baz);

但是,如果我在 TestManaged::Foo 的开头放置一个断点,调试器会说 str 具有 <undefined value>,并且 length() 调用崩溃,并在以下函数的 <xstring> 标头中出现未定义的引用错误:

size_type length() const _NOEXCEPT
   // return length of sequence
    return (this->_Mysize);

调试器也会为this 指针显示<undefined value>

这可能是什么原因?引用在两个库之间传递时会以某种方式损坏?

(附加信息:我以前不将 NativeCodeBase 项目构建为单独的库,而是将其中的所有源文件链接到 CLI 项目中,并且相同的代码库可以正常工作。自从我配置它后它开始失败被构建到一个单独的库中,并在 CLI 项目中添加了对本机项目的引用。)

【问题讨论】:

是否有任何运行时问题(即在调试器中执行之外)? “未定义的引用错误”很难解释。但这里的基本诊断是 std::string 定义在分隔的两端都不相同。使用不同设置或不同版本的 C++ 库编译代码的副作用。迭代器调试始终是一个强有力的候选者,一个但不是另一个。 真正的函数还有其他参数,比如第二个参数是一个float,也被破坏了,它在被调用方包含一些乱码。 我发现了问题,详细回答中。 【参考方案1】:

问题不在于引用本身。问题出在第二个枚举参数上。枚举类的实现如下所示:

#ifdef _MANAGED
    public
#endif
    enum class MyEnum : unsigned char
    
        Baz = 0,
        Qux = 1
    ;

#ifdef 指令被放在那里是为了在为原生 C++ 构建时创建一个原生枚举,但在为 C++/CLI 构建时创建一个 CLI 枚举。

当所有源文件都链接到 CLI 项目并且每个源代码都为 CLI 项目重新构建时,这很有效。但是,当我想从 CLI 端使用本机库时,这种方法不再适用。

我猜问题是同一个头文件在两个库中构建的不同,所以调用者和被调用者看到的对象的二进制接口不同,因此参数在传递时会出现乱码。这是正确的吗?

我去掉了条件编译的 public 关键字,它又开始正常工作了。

【讨论】:

把这个细节放在答案中真的没有什么意义。只有今天,现在,你才感兴趣。未来没有其他人会从中受益。这个问题无法回答,因为实际问题包含在问题中不存在的代码中。随意删除整个批次。 我认为它可以给其他人一个提示,当函数参数在 C++ 和 C++/CLI 之间互操作时出现乱码时,问题可能是有条件编译的 C++/CLI 扩展。 您为什么不重新编写问题以实际包含有问题的代码。这可能会有所帮助。 我用有关条件编译的枚举类的信息更新了原始问题。 这是一个改进

以上是关于为啥我们不能将本地 C++ 的引用传递给 C++/CLI?的主要内容,如果未能解决你的问题,请参考以下文章

C++:为啥我不能将一对迭代器传递给 regex_search?

将本地 std::vector 分配给 C++ 中的引用

为啥我们需要一个默认构造函数来在 C++ 中通过引用传递一个对象?

C++:为啥对于内置(即类 C)类型,按值传递通常比按引用传递更有效

为啥不允许将数组按值传递给 C 和 C++ 中的函数?

C++ 为啥我不能将此指针保存到引用?