3

I'm writing an UDP client which is receiving complex data structures. I do have the structures in C++, but my program is in C#

There are a lot of different structures with Bitfiels and unions.

Is there any way that I do not have to convert the structs by hand?

Also is there an easy way to implement Bitfields and Unions in C#?

Right now I'm using Properties for Bitfields, but that's some difficult work, with a strong possibility for mistakes.

I provided an simplified example what I'm doing right now, there are about 50 structs with 100 lines of code each.

Example c++:

typedef struct Message_s
{
    unsigned char var : 2;
    unsigned char var2 : 6;

    union
    {
         struct
         {
            unsigned char sVar;
            unsigned char sVar2;
         }
         char arr[32];
     }    
}Message_t;

Example C#:

 [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct Message_s
{
    private byte Field1
    public byte var
    {
       get
       {
           return (byte)(Field1 & 0x03);
       }
       set
       {
           Field1 = (byte)((Field1 & ~0x03) | value);
       }
    public byte var2
    {
       get
       {
           return (byte)(Field1 & 0xFC);
       }
       set
       {
           Field1 = (byte)((Field1 & 0x03) | value<<2);
       }
     }
//unions may be possible with properties...
}
Christian Elsner
  • 113
  • 1
  • 4
  • 15
  • Regarding manipulating bit fields, you may want to look at System.Collections.Specialized.BitVector32. Also, depending on what you're doing, you may want to look at using an enum of the appropriate width and decorating it with the Flags attribute. – JamieSee Jun 21 '12 at 16:04
  • ok how about this: I write a manged C++ DLL which incorperates all the structs, that would allow me to access them from C#, wouldn't it? Also, another question, does the C++ compiler keep the byte and bit order of the structs? – Christian Elsner Jun 22 '12 at 07:23
  • Unfortunately, @ChristianElsner, I had the same thought and this doesn't seem to work. These kind of structs compile, but their members are not accessible from C#. You make it a managed struct by replacing `struct` with `value class`, but then you get compiler errors for both bitfields and unions. – Kara Potts Jun 22 '12 at 07:54
  • It might be possible to use COM to access your C++ structure no ? – lollancf37 Jun 22 '12 at 14:01

2 Answers2

4

The short answer is no, there doesn't seem to be an easy way of getting C++ structs into a C# format. There is, however, a way of doing unions in C# structs; whether this is a good idea, or if it's better to start from scratch with classes, will depend on you.

To get unions in C# structs you use LayoutKind.Explicit and the FieldOffset attribute:

[StructLayout(LayoutKind.Explicit)]
struct StructTest
{
    // A byte at the beginning of the struct.
    [FieldOffset(0)]
    public byte byte1;

    // Another byte immediately after it.
    [FieldOffset(1)]
    public byte byte2;

    // Now an integer which covers both.
    [FieldOffset(0)]
    public Int16 int1;
}

You can use this how you would expect:

        var foo = new StructTest();
        foo.byte1 = 3;
        foo.byte2 = 6;
        Console.WriteLine("{0}", foo.int1);

        var bar = new StructTest();
        bar.int1 = 5050;
        Console.WriteLine("{0}, {1}", bar.byte1, bar.byte2);

produces 1539 and 186, 19.

This gets you the union structure, but it still doesn't answer the main problem of BitFields. There doesn't seem to be a consensus on this (except, of course, what you've already done; see, for example, [1]). It might be possible to do something clever with the MarshalAs attribute to map a custom BitField class onto the underlying struct, but at that level of effort it might be easier to keep writing custom classes as you have been.

Community
  • 1
  • 1
Kara Potts
  • 1,062
  • 9
  • 15
2

As an alternative, there is a way of using unmanaged C++ types in your code, but it does still involve some typing. In a VS C++ project, create your unmanaged struct:

struct Date_s
{
    union
    {
        struct
        {
            unsigned nWeekDay  : 3;    // 0..7   (3 bits)
            unsigned nMonthDay : 6;    // 0..31  (6 bits)
            unsigned           : 0;    // Force alignment to next boundary.
            unsigned nMonth    : 5;    // 0..12  (5 bits)
            unsigned nYear     : 8;    // 0..100 (8 bits)
        };
        unsigned bob;
    };
};

Now we create a managed type which wraps this structure:

public ref struct Date_ms
{
    AutoPtr<Date_s> data;
    Date_ms()
    {
        data = new Date_s();
    }

    property unsigned nWeekDay
    {
        unsigned get() { return data->nWeekDay; }
        void set(unsigned value) { data->nWeekDay = value; }
    }
    property unsigned nMonthDay
    {
        unsigned get() { return data->nMonthDay; }
        void set(unsigned value) { data->nMonthDay = value; }
    }
    property unsigned nMonth
    {
        unsigned get() { return data->nMonth; }
        void set(unsigned value) { data->nMonth = value; }
    }
    property unsigned nYear
    {
        unsigned get() { return data->nYear; }
        void set(unsigned value) { data->nYear = value; }
    }
    property unsigned bob
    {
        unsigned get() { return data->bob; }
        void set(unsigned value) { data->bob = value; }
    }
};

There's some copy and pasting to do there. There's also an external item I use here: it's AutoPtr from Kerr. I've also added the line T* operator=(T* rhs) { return (m_ptr = rhs); } to the AutoPtr code (as suggested in the comments to Kerr's post) to make the initialiser work.

You can now use this new structure in your C# code, and everything should work as expected:

var test = new Date_ms();
test.nMonth = 3;
test.nMonthDay = 28;
test.nYear = 98;
Console.WriteLine("{0}, {1}, {2}", test.nMonth, test.nMonthDay, test.nYear);
Console.WriteLine("{0}", test.bob);

results in 3, 28, 98 and 224.

BIG GIANT WARNING

Mixing managed and unmanaged code like this is dangerous, which is why Kerr wrote AutoPtr in the first place (BTW, his blog post on the topic is well worth a read). This works for me, but I can't guarantee that it won't cause memory leaks. You should definately check that this works as intended before deploying it.

Kara Potts
  • 1,062
  • 9
  • 15