c++pinvoke

Lifetime of a temporary


I am encountering some sporadic crashes when calling into a C++ library (not under my control) from C#.

The internal code of the C++ library is roughly like this:

#include <string>
#include <memory>

class Holder {
    public:
    static std::shared_ptr<Holder> create() {
        return std::make_shared<Holder>();
    }

    const std::string& getRef() {
        return value;
    }

    private:
    std::string value;
};

extern "C" void getRef(std::string& v) {
    auto h = Holder::create();
    v = h->getRef();
}

getRef is called from C# via P/invoke with no special marshalling.

Is this guaranteed to work? h goes out of scope at the end of getRef, but a reference to its member value is held and passed on. Is this reference still valid when being access on the C# side?


Solution

  • The C++ part is fine.

    v = h->getRef();

    getRef returns a reference to a string that will be valid until h is killed off, which happens at the end of the function. However, the string is copied into the v string in this line, so no problem.

    h goes out of scope at the end of getRef, but a reference to its member value is held and passed on.

    That's where you are wrong; that reference is not passed on to anything.


    EDIT: concerning the comments that this doesn't copy anything, try this:

    #include <string>
    #include <iostream>
    
    const std::string& getRef()
    {
        static const std::string result("success!");
        return result;
    }
    
    void test(std::string& v)
    {
        v=getRef();
    }
    
    int main()
    {
        std::string string="failure!";
        test(string);
        std::cout << string << std::endl;
        return 0;
    }
    
    stieber@gatekeeper:~ $ g++ Test.cpp; ./a.out
    success!
    

    Basically, while a reference is functionally similar to a pointer, it can't be assigned to: if you assign to a reference, you're always assigning to the object that's being referenced.