Implicit user-defined cast operator (sometimes) does not work for the const type - c++-cli

I'm attempting to make some of our IJW wrappers between our C++ and our .NET code easier to use.
The following is a test situation with our wrapper platform:
// NativeWrapper.h
#pragma once
template <class T>
public ref class NativeWrapper
{
public:
// ...
operator T* ();
operator T& ();
// ...
};
// Test.h
#pragma once
class Test { /* ... */ };
#pragma make_public( Test )
// TestN.h
#pragma once
#include "NativeWrapper.h"
#include "Test.h"
public ref class TestN : public NativeWrapper<Test>
{ /* ... */ };
// main.cpp
#include "TestN.h"
void f(const Test&) { /* ... */ }
int main()
{
TestN^ t = gcnew TestN();
f(t);
return 0;
}
The implicit cast from TestN^ to const Test& via the user-defined operator Test&() works just fine, as I had expected.
However, when I try this in my production code, something is different. It cannot find the proper conversion. I must do the following:
f((Test&)t);
I haven't been able to identify any fundamental differences between what I'm doing in my test and what I'm doing in the main codebase. The NativeWrapper template is identical.
What types of situations might cause the compiler to fail to use a non-const conversion operator when the only acceptable type is the const reference type?
Thanks.

Related

How to set embedded struct fields in Kotlin/Native

Kotlin cinterop has generated such a wrapper for a C struct:
#kotlinx.cinterop.internal.CStruct public final class _GtkAccelGroupEntry public constructor(rawPtr: kotlinx.cinterop.NativePtr /* = kotlin.native.internal.NativePtr */) : kotlinx.cinterop.CStructVar {
#kotlinx.cinterop.internal.CStruct.VarType public companion object : kotlinx.cinterop.CStructVar.Type {
}
public final var accel_path_quark: gtk3.GQuark /* = kotlin.UInt */ /* compiled code */
public final var closure: kotlinx.cinterop.CPointer<gtk3.GClosure /* = gtk3._GClosure */>? /* compiled code */
public final val key: gtk3.GtkAccelKey /* = gtk3._GtkAccelKey */ /* compiled code */
}
The last field (key) is val, I assume because it's also a structure wrapper.
Is there any way to initialize such a field without resorting to unsafe memory access?
here's the original C struct:
struct _GtkAccelKey
{
guint accel_key;
GdkModifierType accel_mods;
guint accel_flags : 16;
};
struct _GtkAccelGroupEntry
{
GtkAccelKey key;
GClosure *closure;
GQuark accel_path_quark;
};
Sorry, but, as far as I know, there are no approaches available other than direct manipulation. I found a discussion at the kotlinlang Slack, might be related.

cpp/cli interop dll breaks in visual studio 2015

I've been assigned the task of upgrading an old interop dll into visualstudio2015 with the v140 toolset.
I'm getting an odd access violation now.
Exception thrown at 0x000007fef7d3d95c (mscoreei.dll) in HostApp.exe:
0xC0000005: Access violation reading location 0xffffffffffffffff
Did anything change in cpp/cli from v110 to v140?
I can tell the error occurs when the methods are defined in a separate cpp instead of inline in the Foo.h. This work-around isn't possible since this is where I need to call managed objects.
Here is my attempt at reconstructing the outline of the code:
shared header IFoo.h
#ifdef DLL_EXPORTS
#define __API__ __declspec(dllexport)
#else
#define __API__ __declspec(dllimport)
#endif
class IFoo
{
public:
__API__ static IFoo* Create();
virtual void DoSomething()=0;
};
Foo.h
#include IFoo.h
class Foo : public IFoo
{
public:
Foo() { } // if inline no crash
void DoSomething();
};
Foo.cpp
#include ManagedStuff.h
#include Foo.h
// Foo::Foo() { } // AV if not inline.
void Foo::DoSomething()
{
gcroot<ManagedClass^> stuff = gcnew ManagedClass();
.....
}
FooImpl.cpp
#include Foo.h
IFoo* IFoo::Create()
{
IFoo* f = new Foo(); // CRASH if ctor is NOT inline.
f->DoSomething(); //CRASH
return f;
}

Why a serializable class must have the function that get the instance from class name?

Such as mfc, it should add
DECLARE_SERIAL(CGraph)
If I a have a class,
class A
{
int a,b;
};
I can store the value of a and b to a file ,then read it.
So I couldn't understand why DECLARE_SERIAL(CGraph) used.
The DECLARE_SERIAL and IMPLEMENT_SERIAL macros are only necessary for classes derived from CObject that you wish to serialize polymorphically using the framework provided by MFC.
If your class is not derived from CObject and/or you do not wish to use MFC's serialization polymorphically (i.e. via a pointer to CObject), then of course you can implement your own solution as you rightly say.
For example, DECLARE_SERIAL(CMyClass) expands to the following code that goes in your class declaration
protected:
static CRuntimeClass* __stdcall _GetBaseClass();
public:
static CRuntimeClass classCMyClass;
static CRuntimeClass* __stdcall GetThisClass();
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* __stdcall CreateObject();
friend CArchive& __stdcall operator>>(CArchive& ar, CMyClass* &pOb);
and IMPLEMENT_SERIAL(CMyClass, CObject, VERSIONABLE_SCHEMA | 1) expands to the following code that goes in the cpp file
CObject* __stdcall CMyClass::CreateObject()
{
return new CMyClass;
}
extern AFX_CLASSINIT _init_CMyClass;
CRuntimeClass* __stdcall CMyClass::_GetBaseClass()
{
return (CObject::GetThisClass());
}
__declspec(selectany) CRuntimeClass CMyClass::classCMyClass =
{
"CMyClass", sizeof(class CMyClass), (0x80000000) | 1,
CMyClass::CreateObject, &CMyClass::_GetBaseClass, 0, &_init_CMyClass
};
CRuntimeClass* __stdcall CMyClass::GetThisClass()
{
return ((CRuntimeClass*)(&CMyClass::classCMyClass));
}
CRuntimeClass* CMyClass::GetRuntimeClass() const
{
return ((CRuntimeClass*)(&CMyClass::classCMyClass));
}
AFX_CLASSINIT _init_CMyClass((CMyClass::GetThisClass()));
CArchive& __stdcall operator>>(CArchive& ar, CMyClass* &pOb)
{
pOb = (CMyClass*) ar.ReadObject((CMyClass::GetThisClass()));
return ar;
}
As it says in MSDN it is also possible to use serialization without using the above macros:

Handling an Unmanaged String in a C++/CLI Wrapper - BLOCK_TYPE_IS_VALID, _CrtIsValidHeapPointer

I'm new to C++/CLI but have been coding managed code for many years... apparently too many years. :)
Attempting to write a wrapper for an unmanaged class provided by a 3rd party, and I'm seeing some strange stuff. I'm hoping you all can help me out weed what what is my noobishness and what is actually strange.
CLI Wrapper:
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
String^ getSomeString()
{
string x = _unmanagedClass->getSomeString(); //1
String^ ret = gcnew String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
I should also note that I have these directives in the header;
#pragma managed(push, off)
#include "Unmanaged.h"
#pragma comment(lib, "lib\\Unmanaged_dll.lib")
#pragma managed(pop)
Here's Unmanaged.h;
class UNMANGED_API UnmanagedClass
{
public:
UnmanagedClass(const float* flts, uint fltlen, int offset);
string getSomeString() { return _someString; }
private:
string _someString;
};
This all compiles. Then the strangness/lack of experience kicks in.
When debugging this in DEBUG configuration, UnmanagedClass::getSomeString() appears to be returning a resonable/expected string value. I can see this by setting a breakpoint on //2 and peeking the value of x. If I step to //3, I can see that ret has the value of x. However, if I attempt to step out/over //3, I get a couple of failed assertions (BLOCK_TYPE_IS_VALID and _CrtIsValidHeapPointer) and the debugger stalls, never returning to the managed implementation.
When debugging this in RELEASE configuration, I don't get the failed assertions and I return to my managed implementation, but the string value returned by getSomeString() is garbage where ever I peek it; //2, //3 as well as in the managed implementation.
I've massaged the code in a few different ways to no avail.
I think there is some Mashalling that needs to be done around //2, but I haven't been able to find any thing that really hits home as far as how to marshall that basic_string to a System::String^, or if it's even required. If so, then some help with explicit syntax would be greatly appreciated.
I've also narrowed the call that produces the failed assertions down to //1 by returning return ""; //3. These assertions point to trying to modify/delete memory that dosn't exist on the heap the current runtime has access to. Does that have to do with needing to marshall the return value of UnmangedClass::getSomeString()?
Hoping I'm just missing some simple concept here and there isn't a problem with the 3rd party code. Please let me know if I can provide any more detail, and apologies for my almost complete ignorance of the grand-daddy of all languages.
Thanks in advance for any information or "pointers";
EDIT: Adding C# Managed client implementation;
public unsafe string GetString(List<float> flts )
{
float[] fltArr = flts.ToArray();
Wrapper wrap;
fixed (float* ptrFlts = fltArr)
{
wrap = new Wrapper(ptrFlts , fltArr.Length, 0);
}
var x = wrap.getSomeString();
return x.ToString();
}
EDIT: Adding Dumpbin.exe signature of Unmanged.dll!UnmangedClass::getSomeString()
(public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall Codegen::getSomeString(void))
This problem has nothing to do with .NET or C++/CLI, the problem is purely in the native code.
You've violated the One Definition Rule for std::string, if your definition doesn't exactly match what Unmanaged_dll.dll uses, all hell breaks loose. And it sounds as if that DLL is used the debug definition/class layout.
You converted your native string to a Managed string just fine. This article on MSDN, has samples on how to convert between all the different string types that ships on Microsoft platforms:
Having said that, I took your code, and compiled it, and I couldn't get anything to fail. Of course I had to come up with my own way of initializing UnmanagedClass::_someString, which I did by just doing this:
UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
_someString = "A few of my favorite things";
}
When I did that, and stepped through this code:
#include "stdafx.h"
#include "Wrapper.h"
int _tmain(int argc, _TCHAR* argv[])
{
Wrapper^ w = gcnew Wrapper(NULL, 0, 0);
System::String^ s = w->getSomeString();
return 0;
}
It worked just fine.
Here is the rest of what I did:
// UnmanagedClass.h
#pragma once
#pragma unmanaged
#include <vector>
class UnmanagedClass
{
public:
UnmanagedClass(const float* flts, unsigned int fltlen, int offset);
std::string getSomeString() { return _someString; }
private:
std::string _someString;
};
And it's implementation:
// UnmanagedClass.cpp
#include "UnmanagedClass.h"
#include <tchar.h>
UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
_someString = "A few of my favorite things";
}
And the managed class
// wrapper.h
#pragma once
#pragma unmanaged
#include "UnmanagedClass.h"
#pragma managed
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
System::String^ getSomeString()
{
std::string x = _unmanagedClass->getSomeString(); //1
System::String^ ret = gcnew System::String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
I hope that helps a little.

C++/CLI, "constant" reference to a tracking handle

I have spotted something like this in code:
void foo(IList<int>^ const & list ) { ... }
What does this ^ const& mean? I looked in the C++/CLI specification, but found no comments on making constant tracking references, nor the ^& combo.
Is this legal?
This code was probably written by a C++ programmer that used common C++ idiom to write C++/CLI. It is quite wrong, passing a reference to tracking handle is only possible if the handle is stored on the stack. It cannot work if the passed List<> reference is stored in a field of an object on the heap, the garbage collector can move it and make the pointer invalid. The compiler will catch it and generate an error. The ^ is already a reference, no additional reference is needed.
Without the reference, the const keyword doesn't make a lot of sense anymore either. Not that it ever did before, the CLR cannot enforce it. Not that this mattered much here, this code could not be called from any other .NET language. They won't generate a pointer to the tracking handle.
Just fix it, there's little point in keeping bad code like this:
void foo(IList<int>^ list ) { ... }
Example of code that shows that the reference cannot work:
using namespace System;
using namespace System::Collections::Generic;
ref class Test {
public:
IList<int>^ lst;
void foo(IList<int> const &list) {}
void wontcompile() {
foo(lst); // C3699
IList<int>^ okay;
foo(okay);
}
};
It's a reference which is constant to a tracking handle.
It allows you to pass the handle by reference instead of by value. Presumably the author thinks it's more efficient than copying the handle.
If the author meant to make the handle constant he should have used either of
Method(TestClass const ^ const & parameter)
Method(TestClass const^ parameter)
Or alternatively
Method(TestClass const^& parameter) - but the caller must const up the handle first
with
TestClass const^ constHandle = nonConstHandle
An example of each:
// test.cpp : Defines the entry point for the console application.
#include "stdafx.h"
ref class TestClass
{
public:
void setA(int value)
{
a = value;
}
TestClass() :
a(10)
{
}
private:
int a;
};
class TakesHandle
{
public:
void methodX1(TestClass const ^ const & parameter)
{
// Un-commenting below causes compiler error
// parameter->setA(11);
}
void methodX2(TestClass const^ parameter)
{
// Un-commenting below causes compiler error
// parameter->setA(11);
}
void methodX3(TestClass const^& parameter)
{
// Un-commenting below causes compiler error
// parameter->setA(11);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
TakesHandle takes;
TestClass ^ test1 = gcnew TestClass();
// compiles
takes.methodX1(test1);
// compiles
takes.methodX2(test1);
TestClass const ^ constHandle = test1;
takes.methodX3(constHandle);
return 0;
}