none
Implement combination of union and bit field by C#? RRS feed

  • Question

  • Dear All,

    There is a way to implement combination of union and bit field data structure in c, see below

    is it possible to implement this kind of data structure by c#?

    Thanks and regards,

    E-John

    typedef __ARMCC_PACK__ struct _union_bit_field_s {
    	u16 bit0			: 1;
    	u16 bit1			: 1;
    	u16 bit2			: 1;
    	u16 bit3			: 1;
    	u16 bit4			: 1;
    	u16 bit5			: 1;
    	u16 bit6			: 1;
    	u16 bit7			: 1;
    	u16 bit8			: 1;
    	u16 bit9			: 1;
    	u16 bit10			: 1;
    	u16 bit11			: 1;
    	u16 bit12			: 1;
    	u16 bit13			: 1;
    	u16 bit14			: 1;
    	u16 bit15			: 1;
    } __ATTRIB_PACK__ _union_bit_field_s;
    
    typedef __ARMCC_PACK__ union {
    	_union_bit_field_s s;
    	u16 w;
    } union_bit_field_s __ATTRIB_PACK__;


    • Edited by E-John Tuesday, May 22, 2018 3:02 PM
    Tuesday, May 22, 2018 3:02 PM

Answers

  • Check this:

    struct _union_bit_field_s

    {

           UInt16 bits;

     

           public bool this[int i]

           {

                 get

                 {

                        return ( bits & ( 1 << i ) ) != 0;

                 }

                 set

                 {

                        if( value )

                        {

                               bits |= (UInt16)( 1 << i );

                        }

                        else

                        {

                               bits &= (UInt16)~( 1 << i );

                        }

                 }

           }

    }

     

    [StructLayout( LayoutKind.Explicit )]

    struct union_bit_field_s

    {

           [FieldOffset( 0 )]

           public _union_bit_field_s s;

     

           [FieldOffset( 0 )]

           public UInt16 w;

    }

     

     

    Sample:

    var u = new union_bit_field_s();

    u.s[15] = true;

    u.s[4] = true;

    Console.WriteLine( Convert.ToString( u.w, 2 ) );

    Console.WriteLine( u.w );

    if( u.s[4] ) Console.WriteLine( "Bit 4 is set" );

    u.w = 1234;

    // . . .


    • Marked as answer by E-John Wednesday, May 23, 2018 2:44 AM
    Tuesday, May 22, 2018 6:32 PM

  • namespace TestBitField
    {
        public struct _union_bit_field_s
        {
            internal UInt16 raw;
    
            const UInt16 sz0 = 1, loc0 = 0, max0 = ((1 << sz0) - 1), mask0 = max0 << loc0;
            const UInt16 sz1 = 1, loc1 = loc0 + sz0, max1 = ((1 << sz1) - 1), mask1 = max1 << loc1;
            const UInt16 sz2 = 6, loc2 = loc1 + sz1, max2 = ((1 << sz2) - 1), mask2 = max2 << loc2;
            const UInt16 sz3 = 2, loc3 = loc2 + sz2, max3 = ((1 << sz3) - 1), mask3 = max3 << loc3;
            const UInt16 sz4 = 1, loc4 = loc3 + sz3, max4 = ((1 << sz4) - 1), mask4 = max4 << loc4;
            const UInt16 sz5 = 1, loc5 = loc4 + sz4, max5 = ((1 << sz5) - 1), mask5 = max5 << loc5;
            const UInt16 sz6 = 4, loc6 = loc5 + sz5, max6 = ((1 << sz6) - 1), mask6 = max6 << loc6;
    
            public UInt16 Bit0
            {
                get { return (UInt16)((raw & mask0) >> loc0); }
                set
                {
                    if (value > max0) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask0 | (value << loc0) & mask0);
                    }
                }
            }
    
            public UInt16 Bit1
            {
                get { return (UInt16)((raw & mask1) >> loc1); }
                set
                {
                    if (value > max1) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask1 | (value << loc1) & mask1);
                    }
                }
            }
    
            public UInt16 Bit7_2
            {
                get { return (UInt16)((raw & mask2) >> loc2); }
                set
                {
                    if (value > max2) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask2 | (value << loc2) & mask2);
                    }
                }
            }
    
            public UInt16 Bit9_8
            {
                get { return (UInt16)((raw & mask3) >> loc3); }
                set
                {
                    if (value > max3) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask3 | (value << loc3) & mask3);
                    }
                }
            }
    
            public UInt16 Bit10
            {
                get { return (UInt16)((raw & mask4) >> loc4); }
                set
                {
                    if (value > max4) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask4 | (value << loc4) & mask4);
                    }
                }
            }
    
            public UInt16 Bit11
            {
                get { return (UInt16)((raw & mask5) >> loc5); }
                set
                {
                    if (value > max5) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask5 | (value << loc5) & mask5);
                    }
                }
            }
    
            public UInt16 Bit15_12
            {
                get { return (UInt16)((raw & mask6) >> loc6); }
                set
                {
                    if (value > max6) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask6 | (value << loc6) & mask6);
                    }
                }
            }
        }
    
        [StructLayout(LayoutKind.Explicit)]
        struct union_bit_field_s
        {
            [FieldOffset(0)]
            public _union_bit_field_s s;
            [FieldOffset(0)]
            public UInt16 w;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                string binary;
                union_bit_field_s myStruct = new union_bit_field_s();
                int sizeUnionBitFieldStruct = Marshal.SizeOf(typeof(union_bit_field_s));
    
                Console.WriteLine("union_bit_field_s size got by Marshal.SizeOf() = " + sizeUnionBitFieldStruct.ToString());
    
                myStruct.s.Bit0 = 0x01;
                Console.WriteLine("myStruct.s.Bit0 = {0}",  myStruct.s.Bit0);
                myStruct.w = 0x1132;
                Console.WriteLine("myStruct.w = 0x{0:X}", myStruct.w);
                binary = Convert.ToString(myStruct.w, 2);
                Console.WriteLine("myStruct.w = b'{0}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit0, 2).PadLeft(1,'0');
                Console.WriteLine("myStruct.s.Bit0 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit1, 2).PadLeft(1, '0');
                Console.WriteLine("myStruct.s.Bit1 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit7_2, 2).PadLeft(6, '0');
                Console.WriteLine("myStruct.s.Bit7_2 = b'{0,6}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit9_8, 2).PadLeft(2, '0');
                Console.WriteLine("myStruct.s.Bit9_8 = b'{0,2}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit10, 2).PadLeft(1, '0');
                Console.WriteLine("myStruct.s.Bit10 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit11, 2).PadLeft(1, '0');
                Console.WriteLine("myStruct.s.Bit11 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit15_12, 2).PadLeft(4, '0');
                Console.WriteLine("myStruct.s.Bit15_12 = b'{0,4}", binary);
    
                Console.ReadKey();
            }
        }
    }




    • Marked as answer by E-John Wednesday, May 23, 2018 3:01 AM
    • Edited by E-John Wednesday, May 23, 2018 5:29 AM Update screenshot result after modification
    Wednesday, May 23, 2018 2:41 AM

All replies

  • Not directly. C# doesn't support unions at all. You can technically do simple unions by creating a struct and setting the LayoutKind to Explicit. Then set all the fields to the same offset. This works for primitive types but wouldn't work for anything else. It is about as close as you can get to a union in C#.

    In general unions in C++ were used to allow you to store one of several values into a structure and then pass it around as a single type. In C# this is less necessary (but sometimes useful) because you can use interfaces to centralize type references while still using arbitrary types to store the specific data. Except for interop cases I wouldn't use unions in C# at all.

    But one thing I notice about your union is that it is actually a bit field, not a union. It is equivalent to a ushort in .NET so there is really no need for a union/struct at all. If you want to be able to easily access the individual bits then consider using BitArray instead. For your __ATTRIB_PACK__ union it is simply allowing you to access the individual bits or the short. BitArray can do all that already so a union is not necessary.


    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, May 22, 2018 6:18 PM
    Moderator
  • Check this:

    struct _union_bit_field_s

    {

           UInt16 bits;

     

           public bool this[int i]

           {

                 get

                 {

                        return ( bits & ( 1 << i ) ) != 0;

                 }

                 set

                 {

                        if( value )

                        {

                               bits |= (UInt16)( 1 << i );

                        }

                        else

                        {

                               bits &= (UInt16)~( 1 << i );

                        }

                 }

           }

    }

     

    [StructLayout( LayoutKind.Explicit )]

    struct union_bit_field_s

    {

           [FieldOffset( 0 )]

           public _union_bit_field_s s;

     

           [FieldOffset( 0 )]

           public UInt16 w;

    }

     

     

    Sample:

    var u = new union_bit_field_s();

    u.s[15] = true;

    u.s[4] = true;

    Console.WriteLine( Convert.ToString( u.w, 2 ) );

    Console.WriteLine( u.w );

    if( u.s[4] ) Console.WriteLine( "Bit 4 is set" );

    u.w = 1234;

    // . . .


    • Marked as answer by E-John Wednesday, May 23, 2018 2:44 AM
    Tuesday, May 22, 2018 6:32 PM
  • Dear All,

    There is a way to implement combination of union and bit field data structure in c, see below

    is it possible to implement this kind of data structure by c#?

    Thanks and regards,

    E-John

    typedef __ARMCC_PACK__ struct _union_bit_field_s {
    	u16 bit0			: 1;
    	u16 bit1			: 1;
    	u16 bit2			: 1;
    	u16 bit3			: 1;
    	u16 bit4			: 1;
    	u16 bit5			: 1;
    	u16 bit6			: 1;
    	u16 bit7			: 1;
    	u16 bit8			: 1;
    	u16 bit9			: 1;
    	u16 bit10			: 1;
    	u16 bit11			: 1;
    	u16 bit12			: 1;
    	u16 bit13			: 1;
    	u16 bit14			: 1;
    	u16 bit15			: 1;
    } __ATTRIB_PACK__ _union_bit_field_s;
    
    typedef __ARMCC_PACK__ union {
    	_union_bit_field_s s;
    	u16 w;
    } union_bit_field_s __ATTRIB_PACK__;



    I think there might be a way to achive the same kind of thing. As others say C# has no support for the union type but that isn't a show stopper.

    You could have a "union" struct that contains a single raw (unsafe) pointer and then you could have two properties that cast that pointer to a reference to the specific kind of type you wanted, or even a generic method!

    Recent changes to C# make this much easier than before but this would require C# 7.3 the latest compiler version.

    Here's what I mean, this code compiles (with C# 7.3) but won't run because the pointer is null, but if yuou were to pass a read address in it would work - but you must ensure each struct type has the same alignment, I mean you don't want Layout_1 to be aligned  on a 4 byte boundary and Layout_2 to be aligned on an 8 byte boundary...

    namespace NewConsole
    {
        unsafe class Program
        {
            static void Main(string[] args)
            {
                Union union = new Union(null);
    
                // layout1 and layout2 each refer to the same memory address.
    
                var layout1 = union.AccessAs<Layout_1>();
                var layout2 = union.AccessAs<Layout_2>();
    
                layout1.Counter = 23;
                layout2.Rate = 34.5;
            }
        }
        public unsafe class Union
        {
            public void* Address { get; private set; }
            public Union (void * Address)
            {
                this.Address = Address;
            }
    
            public ref T AccessAs<T>() where T : unmanaged
            {
                return ref (*(T*)Address);
            }
        }
        public struct Layout_1
        {
            public long Counter;
            public int Depth;
            public int Other;
        }
        public struct Layout_2
        {
            public byte Byte1;
            public byte Byte2;
            public byte Byte3;
            public byte Byte4;
            public double Rate;
        }
    }


    Tuesday, May 22, 2018 8:50 PM
  • Captain Kernel,

    This question does me think about your "ref" question. Somebody told it was needed. 

    Micheal told it already, but I go always back in the time when this kind of programming was needed. We started with all kind of True/False hardware. My oh my what was that expensive every bit had to be used. 

    This is still the fact for by instance drivers and other kind of small hardware. However, C# with its huge .Net dependency is never made for that. 

    Then why should this kind of bit f....ng still be needed. 

    Nevertheless, maybe do you have a reason which tells that it is a kind of general omission in C#


    Success
    Cor

    Tuesday, May 22, 2018 9:27 PM
  • Hi CoolDadTx,

    Thanks for your reply and advice,

    I think I gave the wrong example, the general case should be as follows,

    Thanks and Best regards,

    E-John

    typedef __ARMCC_PACK__ struct _union_bit_field_s {
    	u16 bit0			: 1;
    	u16 bit1			: 1;
    	u16 bit7_2		: 6;
    	u16 bit9_8		: 2;
    	u16 bit10			: 1;
    	u16 bit11			: 1;
    	u16 bit15_12		: 4;
    } __ATTRIB_PACK__ _union_bit_field_s;
    
    typedef __ARMCC_PACK__ union {
    	_union_bit_field_s s;
    	u16 w;
    } union_bit_field_s __ATTRIB_PACK__;



    • Edited by E-John Wednesday, May 23, 2018 5:30 AM
    Wednesday, May 23, 2018 1:13 AM
  • The bits involved don't really matter. This is the kind of problem BitArray was designed to solve. Or use the more efficient BitVector32 class since you are below 32 bits. Since it is a value type it is efficient. Take a look at the documentation to see how you could use it for arbitrary bit patterns.


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, May 23, 2018 2:12 AM
    Moderator

  • namespace TestBitField
    {
        public struct _union_bit_field_s
        {
            internal UInt16 raw;
    
            const UInt16 sz0 = 1, loc0 = 0, max0 = ((1 << sz0) - 1), mask0 = max0 << loc0;
            const UInt16 sz1 = 1, loc1 = loc0 + sz0, max1 = ((1 << sz1) - 1), mask1 = max1 << loc1;
            const UInt16 sz2 = 6, loc2 = loc1 + sz1, max2 = ((1 << sz2) - 1), mask2 = max2 << loc2;
            const UInt16 sz3 = 2, loc3 = loc2 + sz2, max3 = ((1 << sz3) - 1), mask3 = max3 << loc3;
            const UInt16 sz4 = 1, loc4 = loc3 + sz3, max4 = ((1 << sz4) - 1), mask4 = max4 << loc4;
            const UInt16 sz5 = 1, loc5 = loc4 + sz4, max5 = ((1 << sz5) - 1), mask5 = max5 << loc5;
            const UInt16 sz6 = 4, loc6 = loc5 + sz5, max6 = ((1 << sz6) - 1), mask6 = max6 << loc6;
    
            public UInt16 Bit0
            {
                get { return (UInt16)((raw & mask0) >> loc0); }
                set
                {
                    if (value > max0) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask0 | (value << loc0) & mask0);
                    }
                }
            }
    
            public UInt16 Bit1
            {
                get { return (UInt16)((raw & mask1) >> loc1); }
                set
                {
                    if (value > max1) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask1 | (value << loc1) & mask1);
                    }
                }
            }
    
            public UInt16 Bit7_2
            {
                get { return (UInt16)((raw & mask2) >> loc2); }
                set
                {
                    if (value > max2) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask2 | (value << loc2) & mask2);
                    }
                }
            }
    
            public UInt16 Bit9_8
            {
                get { return (UInt16)((raw & mask3) >> loc3); }
                set
                {
                    if (value > max3) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask3 | (value << loc3) & mask3);
                    }
                }
            }
    
            public UInt16 Bit10
            {
                get { return (UInt16)((raw & mask4) >> loc4); }
                set
                {
                    if (value > max4) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask4 | (value << loc4) & mask4);
                    }
                }
            }
    
            public UInt16 Bit11
            {
                get { return (UInt16)((raw & mask5) >> loc5); }
                set
                {
                    if (value > max5) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask5 | (value << loc5) & mask5);
                    }
                }
            }
    
            public UInt16 Bit15_12
            {
                get { return (UInt16)((raw & mask6) >> loc6); }
                set
                {
                    if (value > max6) { throw new OverflowException(); }
                    else
                    {
                        raw = (UInt16)(raw & ~mask6 | (value << loc6) & mask6);
                    }
                }
            }
        }
    
        [StructLayout(LayoutKind.Explicit)]
        struct union_bit_field_s
        {
            [FieldOffset(0)]
            public _union_bit_field_s s;
            [FieldOffset(0)]
            public UInt16 w;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                string binary;
                union_bit_field_s myStruct = new union_bit_field_s();
                int sizeUnionBitFieldStruct = Marshal.SizeOf(typeof(union_bit_field_s));
    
                Console.WriteLine("union_bit_field_s size got by Marshal.SizeOf() = " + sizeUnionBitFieldStruct.ToString());
    
                myStruct.s.Bit0 = 0x01;
                Console.WriteLine("myStruct.s.Bit0 = {0}",  myStruct.s.Bit0);
                myStruct.w = 0x1132;
                Console.WriteLine("myStruct.w = 0x{0:X}", myStruct.w);
                binary = Convert.ToString(myStruct.w, 2);
                Console.WriteLine("myStruct.w = b'{0}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit0, 2).PadLeft(1,'0');
                Console.WriteLine("myStruct.s.Bit0 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit1, 2).PadLeft(1, '0');
                Console.WriteLine("myStruct.s.Bit1 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit7_2, 2).PadLeft(6, '0');
                Console.WriteLine("myStruct.s.Bit7_2 = b'{0,6}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit9_8, 2).PadLeft(2, '0');
                Console.WriteLine("myStruct.s.Bit9_8 = b'{0,2}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit10, 2).PadLeft(1, '0');
                Console.WriteLine("myStruct.s.Bit10 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit11, 2).PadLeft(1, '0');
                Console.WriteLine("myStruct.s.Bit11 = b'{0,1}", binary);
    
                binary = Convert.ToString(myStruct.s.Bit15_12, 2).PadLeft(4, '0');
                Console.WriteLine("myStruct.s.Bit15_12 = b'{0,4}", binary);
    
                Console.ReadKey();
            }
        }
    }




    • Marked as answer by E-John Wednesday, May 23, 2018 3:01 AM
    • Edited by E-John Wednesday, May 23, 2018 5:29 AM Update screenshot result after modification
    Wednesday, May 23, 2018 2:41 AM
  • Hi Viorel_,

    Thanks for your helps which gives me the direction to follow.

    I have referred your advice and the following link, and I modified a general case(bit field may be greater than one bit) based on your code.

    https://stackoverflow.com/questions/14464/bit-fields-in-c-sharp

    Overflow check also added in each set() mutator to avoid setting value carelessly.


    Thanks and Best regards,

    E-John



    • Edited by E-John Wednesday, May 23, 2018 5:34 AM
    Wednesday, May 23, 2018 2:44 AM