Converting std::array to C# array using SWIG - c#

I'm trying to wrap a C++ class using std::array. In C# I'd like to use this std::array as a normal array. The C++ class could look like this:
#pragma once
#include <array>
struct Data
{
int member1, member2;
};
class TestClass
{
public:
std::array<Data, 2> GetExampleArray() {
Data item1;
Data item2;
item1.member1 = 0; // Initialize with some boring data
item1.member2 = 1;
item2.member1 = 3;
item2.member2 = 4;
return { item1, item2 };
}
};
Usage in C# would be as follows:
class Program
{
static void Main(string[] args)
{
TestClass testClass = new TestClass();
Data myData = testClass.GetExampleArray()[0];
Console.WriteLine(myData.member1);
}
}
My first thought was "Let's pass a pointer to C# using typemaps!" until I realized that this is no C-style array and even if it was I wouldn't be able to pass the length.
So my question is: Which steps can I take to get its data into a C# array?

Related

C# anonymous value types nature

Consider the flowing code snippet
static void Main()
{
var x = new MyStruct
{
Item = new StringWrapper("abc")
};
Console.WriteLine(x.Item.PublicField);
x.Item.SetValue("xyz");
Console.WriteLine(x.Item.PublicField);
var y = new
{
Item = new StringWrapper("abc")
};
Console.WriteLine(y.Item.PublicField);
y.Item.SetValue("xyz");
Console.WriteLine(y.Item.PublicField);
}
public struct MyStruct
{
public StringWrapper Item;
}
public struct StringWrapper
{
public string PublicField;
public StringWrapper(string v)
{
PublicField = v;
}
public void SetValue(string v)
{
PublicField = v;
}
}
And the output:
abc
xyz
abc
abc
MyStruct can be declared as class, and the output will remain the same.
{abc, abc} part of output is a surprise for me, as I expect anonymous type to be converted to class or struct and behave the same.
I feel like I'm missing something obvious here and will appreciate any help.
Thanks.
The difference here is that your MyStruct (struct or class) exposes a public field while anonymous classes (new { }) expose public properties.
When you access a value type from a field or variable, you do not get a copy, you access the instance directly. Therefore making changes to it stores them in the instance.
When you instead access it via a property or method, you get a returned copy of your StringWrapper and changing that doesn't change what is stored in the anonymous classes private field.
Just to demonstrate, you can get the same behavior by making your Item field a property too:
public StringWrapper Item { get; set; }

C# to VB6 interop: indexed array properties are not accessible across nested COM interfaces

I would like to publish a C# class library via COM interop, so that it may be used by VB6 clients. For demonstration purposes, consider the following snippet:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace cslib
{
[ComVisible(true)]
[Guid("...")]
public interface IClass2
{
string m_sStr { get; set; }
}
[ComVisible(true)]
[Guid("...")]
[ClassInterface(ClassInterfaceType.None)]
public class Class2 : IClass2
{
public Class2()
{
m_sStr = "Hello";
}
public string m_sStr { get; set; }
}
[ComVisible(true)]
[Guid("...")]
public interface IClass1
{
Class2[] m_Class2Instances { get; set; }
}
[ComVisible(true)]
[Guid("...")]
[ClassInterface(ClassInterfaceType.None)]
public class Class1 : IClass1
{
public Class1()
{
List<Class2> lstClass2 = new List<Class2>();
for (int i = 0; i < 5; i++)
lstClass2.Add(new Class2());
m_Class2Instances = lstClass2.ToArray();
}
public Class2[] m_Class2Instances { get; set; }
}
}
After registering the library on the system, the COM interface is usable in VB6. However, accessing the array property with an indexed value doesn't seem to work:
Dim c1 As New cslib.Class1
Dim str As String
'Error message = Wrong number of arguments or invalid property assignment
str = c1.m_Class2Instances(0).m_sStr
The only alterative I've found is creating a temporary array variable. This allows me to access the indexed interface properly:
Dim c1 As New cslib.Class1
Dim str As String
'Works, but requires detour via temp array
Dim arrc2() As cslib.Class2
arrc2 = c1.m_Class2Instances
str = arrc2(0).m_sStr
Unfortunately, the array I've illustrated here is a mere simplification. In real life, I need to access COM interfaces, which are located deep inside nested array structures. For instance, the following class structure...
Class1
Class2[]
Class3[]
Class4[]
...would ultimately force me to create three temporary array variables, before I can actually access an instance of Class4.
Is this a limitation of VB6? If so, is there any way to access nested array interfaces without cluttering the code with temporary variables?
I've retired VB6 a long time ago and can't check this anymore. But it is almost certainly suffering from a syntax ambiguity. Did you mean to index the property or the return value of the property? You like the latter interpretation but that's not what it does. You'd have to write something like this:
str = (c1.m_Class2Instances)(0).m_sStr
or:
str = c1.m_Class2Instances()(0).m_sStr
No real idea if this can compile. Bleh anyway.
You need to get ahead by actually writing an indexed property. Like this:
public interface IClass1 {
Class2 this[int index] { get; set; }
}
public class Class1 : IClass1 {
private List<Class2> lstClass2 = new List<Class2>();
public Class1() {
for (int i = 0; i < 5; i++) lstClass2.Add(new Class2());
}
public Class2 this[int index] {
get { return lstClass2[index]; }
set { lstClass2[index] = value; }
}
}
Note how this also makes it a lot less painful to let the VB6 code iterate the array. You are not creating a new array over and over again. The VB6 code gets short and sweet as well:
Dim c1 As New cslib.Class1
Dim str = c1(0).m_sStr
If you really, really need to return or assign a complete array then you must use a method instead.

C# how to invoke a field initializer using reflection?

Say I have this C# class
public class MyClass {
int a;
int[] b = new int[6];
}
Now say I discover this class using reflection and while looking at the fields I find that one of them is of type Array (ie: b)
foreach( FieldInfo fieldinfo in classType.GetFields() )
{
if( fieldInfo.FieldType.IsArray )
{
int arraySize = ?;
...
}
}
I know it's not guaranteed that the array has a field initializer that creates the array but if it does I would like to know the size of the array created by the field initializer.
Is there a way to call the field initializer ?
If there was I would do something like this:
Array initValue = call field initializer() as Array;
int arraySize = initValue.Length;
The only was I found is to create an instance of the whole class but I would rather not do it like this as it's overkill...
Well, you can't.
The following code:
public class Test
{
public int[] test = new int[5];
public Test()
{
Console.Read();
}
}
will be compiled as:
public class Program
{
public int[] test;
public Program()
{
// Fields initializers are inserted at the beginning
// of the class constructor
this.test = new int[5];
// Calling base constructor
base.ctor();
// Executing derived class constructor instructions
Console.Read();
}
}
So, until you create an instance of the type, there is no way to know about the array size.
I dont think you have an option but to create an instance of the class as it doesnt exist until you do that.

Creating a copy of an object in C# [duplicate]

This question already has answers here:
How do you do a deep copy of an object in .NET? [duplicate]
(10 answers)
Closed 3 years ago.
Please have a look at the code below (excerpt from a C# book):
public class MyClass
{
public int val;
}
public struct myStruct
{
public int val;
}
public class Program
{
private static void Main(string[] args)
{
MyClass objectA = new MyClass();
MyClass objectB = objectA;
objectA.val = 10;
objectB.val = 20;
myStruct structA = new myStruct();
myStruct structB = structA;
structA.val = 30;
structB.val = 40;
Console.WriteLine("objectA.val = {0}", objectA.val);
Console.WriteLine("objectB.val = {0}", objectB.val);
Console.WriteLine("structA.val = {0}", structA.val);
Console.WriteLine("structB.val = {0}", structB.val);
Console.ReadKey();
}
}
I understands it produces the output below:
objectA.val = 20
objectB.val = 20
structA.val = 30
structB.val = 40
The last two lines of the output I have no problem with, but the first two tell me that objectA and objectB are pointing to the same memory block (since in C#, objects are reference types).
The question is how do make objectB, a copy of objectA so that it points to a different area in memory. I understand that trying to assign their members may not work since those members may be references, too. So how do I go about making objectB a completely different entity from objectA?
You could do:
class myClass : ICloneable
{
public String test;
public object Clone()
{
return this.MemberwiseClone();
}
}
then you can do
myClass a = new myClass();
myClass b = (myClass)a.Clone();
N.B. MemberwiseClone() Creates a shallow copy of the current System.Object.
There is no built-in way. You can have MyClass implement the IClonable interface (but it is sort of deprecated) or just write your own Copy/Clone method. In either case you will have to write some code.
For big objects you could consider Serialization + Deserialization (through a MemoryStream), just to reuse existing code.
Whatever the method, think carefully about what "a copy" means exactly. How deep should it go, are there Id fields to be excepted etc.
The easiest way to do this is writing a copy constructor in the MyClass class.
Something like this:
namespace Example
{
class MyClass
{
public int val;
public MyClass()
{
}
public MyClass(MyClass other)
{
val = other.val;
}
}
}
The second constructor simply accepts a parameter of his own type (the one you want to copy) and creates a new object assigned with the same value
class Program
{
static void Main(string[] args)
{
MyClass objectA = new MyClass();
MyClass objectB = new MyClass(objectA);
objectA.val = 10;
objectB.val = 20;
Console.WriteLine("objectA.val = {0}", objectA.val);
Console.WriteLine("objectB.val = {0}", objectB.val);
Console.ReadKey();
}
}
output:
objectA.val = 10
objectB.val = 20
There's already a question about this, you could perhaps read it
Deep cloning objects
There's no Clone() method as it exists in Java for example, but you could include a copy constructor in your clases, that's another good approach.
class A
{
private int attr
public int Attr
{
get { return attr; }
set { attr = value }
}
public A()
{
}
public A(A p)
{
this.attr = p.Attr;
}
}
This would be an example, copying the member 'Attr' when building the new object.

C# compiler error: "cannot have instance field initializers in structs"

I need advice on structures.
I have 2 sections of code. The first section is as below:
namespace Project.GlobalVariables
{
class IOCard
{
struct InputCard
{
public string CardNo;
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo = new int[16];
public int[] ReadBitNo = new int[16];
}
static InputCard[] InputCards = new InputCard[5];
public static string ACardNo = InputCards[1].CardNo;
public static string BCardNo = InputCards[2].CardNo;
}
}
The second portion is as below:
private void Form1_Load(object sender, EventArgs e)
{
IOCard.ACardNo = "Card A";
IOCard.BCardNo = "Card B";
MessageBox.Show(IOCard.ACardNo);
MessageBox.Show(IOCard.BCardNo);
}
My plan is to be able to assign and retrieve InputCards component by using IOCard as shown in Form1_Load.
However, when I compile the code, I get the following error.
Error 1 'Project.GlobalVariables.IOCard.InputCard.WriteBitNo': cannot have instance field initializers in structs E:\Programming\New platform\StandardPlatform\StandardPlatform\Project\GlobalVariables.cs 16 26 StandardPlatform
Can someone tell me how to solve the error?
Please advise. Thanks.
Here are the classes that I have attempted to create and use, but failed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Project.GlobalVariables
{
static class IOCard
{
public const int TotalInputCard = 10;
public const int TotalOutputCard = 10;
public class InputCard
{
public string CardNo = "1";
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo = new int[16];
public int[] ReadBitNo = new int[16];
}
public class OutputCard
{
public string CardNo;
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo = new int[16];
public int[] ReadBitNo = new int[16];
}
public static InputCard[] InputCards = new InputCard[TotalInputCard];
public static OutputCard[] OutputCards = new OutputCard[TotalOutputCard];
public static int X100 = InputCards[0].WriteBitNo[0];
public static int Y100 = OutputCards[0].WriteBitNo[0];
}
}
I tried to use these in the Form_Load, like so:
private void Form1_Load(object sender, EventArgs e)
{
IOCard.X100 = 1;
IOCard.Y100 = 1;
}
No matter how much I have tried to search on the net for answers, I have got nowhere.
Please advise. Thanks.
In C#, a struct value is not a reference to an object in the way a value of a class type is. The value of a struct is the "union" of all the values of the instance fields of the struct.
Now, the default value of a struct type is the value where all those fields have their default values. Since the beginning of C#, the syntax:
new S() // S is a value-type
where S is a struct type, has been equivalent to the default value of that struct. There is no constructor call! This is the exact same value which can (nowadays) also be written
default(S) // S is a value-type
Now, things like
struct S
{
int field = 42; // non-static field with initializer, disallowed!
// ...
}
are illegal (cannot have instance field initializers in structs). They could give the impression that the field of a new S() would be 42, but in fact the field of new S() must be the default value of int (which is zero, distinct from 42).
With this explanation, you also see why it is not possible to create a non-static, zero-parameter constructor for a struct type, in C#.
What's it's trying to say is that when you have InputCards = new InputCard[5]; it will allocate a block of memory 5 times the size of an InputCard structure and set all of its bytes to 0. There is no opportunity to execute the int[] WriteBitNo = new int[16]; and such assignments, so you cannot have them.
Your options are to either manually call an initializer for your structs or make it a class and manually initialize the InputCards array with 5 new instances of InputCard.
You will neither be able to initialize a struct's fields nor define a default constructor to initialize it's fields. After looking at your struct, I recommend you use a class instead. It's not recommended to use a struct for a scenario where you have a bunch of fields.
Try this. Initialize the InputCard with a factory function Create():
namespace Project.GlobalVariables
{
class IOCard
{
struct InputCard
{
public string CardNo;
public int BaseAddress;
public int LowerAddress;
public int UpperAddress;
public int[] WriteBitNo;
public int[] ReadBitNo;
static InputCard Create()
{
return new InputCard()
{
CardNo = string.Empty,
WriteBitNo = new int[16],
ReadBitNo = new int[16]
};
}
}
static InputCard[] InputCards = new InputCard[]
{
InputCard.Create(),
InputCard.Create(),
InputCard.Create(),
InputCard.Create(),
InputCard.Create()
};
public static string ACardNo = InputCards[1].CardNo;
public static string BCardNo = InputCards[2].CardNo;
}
}
Use class instead of structure. Structure is used for small types like Point, which are faster to create on the stack and copy, than to create dynamically and pass by reference.
Not sure about the exception, but i have a solution.
You should not use "struct" for this class, it is too much (and storing too much data). If you define it as "class", the same code would work fine.
Is there a particular reason why you want this to be a struct rather than a class?
If you make it a class, it works just fine.

Categories