Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using CS_OUT/CS_IN_OUT macro with value types #1731

Closed
mirkobraic opened this issue Apr 19, 2023 · 3 comments
Closed

Using CS_OUT/CS_IN_OUT macro with value types #1731

mirkobraic opened this issue Apr 19, 2023 · 3 comments

Comments

@mirkobraic
Copy link

mirkobraic commented Apr 19, 2023

Hi, I'm trying to write a function which would return an object using C# out keyword.

Here is C++ code:

namespace testing {
CS_VALUE_TYPE class EXPORT_MACRO value_type {
public:
  value_type() : member(0) {} 
  int member;
};

EXPORT_MACRO void fetch_new_object(my_value_type* input) {
  input->member = 1;
}
}

And this is the generated code:

namespace Testing
{
    public unsafe partial struct MyValueType
    {
        [StructLayout(LayoutKind.Sequential, Size = 4)]
        public partial struct __Internal
        {
            internal int member;

            [SuppressUnmanagedCodeSecurity, DllImport("my_library", EntryPoint = "??0my_value_type@testing@@QEAA@XZ", CallingConvention = __CallingConvention.Cdecl)]
            internal static extern __IntPtr ctor(__IntPtr __instance);

            [SuppressUnmanagedCodeSecurity, DllImport("MyLibrary", EntryPoint = "??0my_value_type@testing@@QEAA@AEBV01@@Z", CallingConvention = __CallingConvention.Cdecl)]
            internal static extern __IntPtr cctor(__IntPtr __instance, __IntPtr _0);
        }

        private MyValueType.__Internal __instance;
        internal MyValueType.__Internal __Instance => __instance;

        internal static MyValueType __CreateInstance(__IntPtr native, bool skipVTables = false)
        {
            return new MyValueType(native.ToPointer(), skipVTables);
        }

        internal static MyValueType __CreateInstance(__Internal native, bool skipVTables = false)
        {
            return new MyValueType(native, skipVTables);
        }

        private MyValueType(__Internal native, bool skipVTables = false)
            : this()
        {
            __instance = native;
        }

        private MyValueType(void* native, bool skipVTables = false) : this()
        {
            __instance = *(global::Testing.MyValueType.__Internal*) native;
        }

        public MyValueType(global::Testing.MyValueType _0)
            : this()
        {
            var ____arg0 = _0.__Instance;
            var __arg0 = new __IntPtr(&____arg0);
            fixed (__Internal* __instancePtr = &__instance)
            {
                __Internal.cctor(new __IntPtr(__instancePtr), __arg0);
            }
        }

        public int Member
        {
            get
            {
                return __instance.member;
            }

            set
            {
                __instance.member = value;
            }
        }
    }

    public unsafe partial class test_header
    {
        public partial struct __Internal
        {
            [SuppressUnmanagedCodeSecurity, DllImport("MyLibrary", EntryPoint = "?fetch_new_object@testing@@YA_NPEAVmy_value_type@1@@Z", CallingConvention = __CallingConvention.Cdecl)]
            [return: MarshalAs(UnmanagedType.I1)]
            internal static extern bool FetchNewObject(__IntPtr input);
        }

        public static void FetchNewObject(out global::Testing.MyValueType input)
        {
            input = new global::Testing.MyValueType();
            var ____arg0 = new global::Testing.MyValueType.__Internal();
            var __arg0 = new __IntPtr(&____arg0);
            __Internal.FetchNewObject(__arg0);
        }
    }
}

When I use this function in the client application, like so: FetchNewObject(out MyValueType myType);, the member value is zero instead of one. It seems that the change is not correctly reflected in the input argument.

Issue could be solved by using C# class instead of struct, but is there a way to make it work with structures?

@tritao
Copy link
Collaborator

tritao commented Jul 12, 2023

The generated code needs to be fixed to work with structs.

@tritao
Copy link
Collaborator

tritao commented Oct 18, 2023

Related: #1772

Might be worth looking if the PR improved anything here, or if the same approach can be used to fix this.

@Saalvage
Copy link
Contributor

public static void FetchNewObject(out global::Testing.ValueType input)
{
    input = new global::Testing.ValueType();
    fixed (global::Testing.ValueType.__Internal* ____arg0 = &input.__Instance)
    {
        var __arg0 = new __IntPtr(____arg0);
        __Internal.FetchNewObject(__arg0);
    }
}

Can confirm the correct code is being generated. (Although CS_OUT needs to be applied to the original C++ source.)

@tritao tritao closed this as completed Oct 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants