Mark Vincze Mark Vincze - 1 month ago 7
C++ Question

Why can't we pass a reference from native C++ to C++/CLI?

I use a native C++ code base from C#, with a C++/CLI wrapper built around it (with Visual Studio 2013). There are two projects:


  • NativeCodeBase: simple C++ project set to be built into a static lib.

  • ManagedWrapper: C++/CLI project referencing NativeCodeBase.



I have the following native "interface" in NativeCodeBase:

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


For which I have a native implementation in the ManagedWrapper project.

In the header:

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


In the cpp:

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


The MyEnum enum is used both in native and managed code, so in its implementation I use a conditionally compiled C++/CLI extension, to make it usable from C#:

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


In my native code I have a reference to
ITest
and call its
Foo
function with a local
std::string
variable. When Foo is called, I can see in the debugger that the string passed as an argument is a valid string object.

The call is similar to this:

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


However, if I put a breakpoint at the beginning of
TestManaged::Foo
, the debugger says that str has
<undefined value>
, and the
length()
call crashes with undefined reference error in the
<xstring>
header in the following function:

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


The debugger displays
<undefined value>
for the
this
pointer as well.

What can be the reason for this? References somehow get corrupted when passed between the two libraries?

(Additional info: I used not to build the NativeCodeBase project as a separate lib, but linked all the source files from it into the CLI project, and the same code base worked without any problem. It started failing since I configured it to be built into a separate lib and added a reference in the CLI project to the native one.)

Answer

The problem wasn't with the reference itself. The problem was with the second enum parameter. The implementation of the enum class looked like this:

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

The #ifdef directive was put there in order to create a native enum when built for native C++, but create a CLI enum when built for C++/CLI.

This worked well when all the source files were linked to the CLI project and every piece of source was built again for the CLI project. However, this approach does not work any more when I want to use the native lib from the CLI side.

I guess the problem was that the same header was built differently in the two libraries, so the caller and the calle saw a different binary interface of the object, thus the arguments got garbled when passed. Is this correct?

I got rid of the conditionally compiled public keyword and it started working properly again.

Comments