1

I'm noob to C#, I've looked at the answers to previous problems similar to this but am still stuck. I need to collect and process 3 arrays of structs that contain arrays of ushort. I made a class to hold my struct:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

struct Wvsamps
{
    ushort[] wav1 = new ushort[3];
    ushort[] wav2 = new ushort[3];
    ushort[] wav3 = new ushort[3];
    ushort[] wav4 = new ushort[3];
    ushort modes;
    uint time;
    ushort chkSum;
}       


namespace ConsoleApplication1
{
    public class LoadSamps
    {
        Wvsamps[] ldSamps = new Wvsamps[0x800000];
    }
}

Compiler gives error:

Error   1   'Wvsamps.wav1': cannot have instance field initializers in structs  C:\Users\Hewitt\Desktop\C# Projects\MarshalBytesToStruct\LoadSamps.cs   8   11  MarshalBytesToStruct

I would then like to access and put values in my arrays of structs from my Main program class. I'm doing this up as a console app to try and get a handle for doing this in a Windows Form app.

To expand upon my problem, I will be receiving 32 byte packets 1/sec over a network connection. The actual data in a 32 byte packet will look like this:

0x94, 0xa5, 0xca, 0x62, 0x41, 0x28, 0x4c, 0x93, 0x09, 0x42, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x04, 0x01, 0x40, 0x10, 0x00, 0x00, 0x70, 0x0d, 0x58, 0x3e, 0xc6, 0xd1, 0x07,

As I receive these 32 byte packets I need to put them in an array of Wvsamps struct as shown in my original post. Ideally the Wvsamps struct is 32bytes long. In an old C/C++ embedded solution of this I was able to memcpy the 32byte network packet to a Wvsamps struct in the ldSamps array then increment a ldSamps array index to point to the next available Wvsamps struct in the ldSamps array for the next received packet. I need to accumulate 0x40000 worth of the Wvsamps structs in the ldSamps array so the array needs to be 0x40000 Wvsamps structs long.

I will be accessing the Wvsamps structs from the ldSamps array to plot waveforms in a WinForm, scroll through the waveforms (I've at least go that working with hard coded simulation data) and make measurements. There will also be the added issue that the data I'm receiving comes in Big Endian format and needs to be converted and stored in Little Endian in the Wvsamps structs.

8
  • Make Wvsamps a class and the problem goes away. Any particular reason you thought you needed a struct? Commented Dec 3, 2016 at 0:01
  • Trying to move a solution from some past C programming. Each struct represents 1 second worth of Wvsamps and I need to collect up to 8meg of them over 72 hours. I left some code out, I need to declare 3 ldSamps[] arrays of Wvsamps structs, each 8Meg in size. I thought I needed to do this by making LoadSamps the class. Commented Dec 3, 2016 at 0:06
  • Modern machines will have no problem with the object sizes you are dealing with. Just use classes and it will allow the initialisation. Commented Dec 3, 2016 at 0:08
  • How do I get an array of WvSamps structs? I tried making it a class as you suggested but now I need 8million instances of it, preferably in an array so I can index through it. Commented Dec 3, 2016 at 0:14
  • Do you need 8 million instances of structs also? Commented Dec 3, 2016 at 0:38

4 Answers 4

1

Here is a solution using fixed arrays that is going to be fast, but clunky.

[StructLayout(LayoutKind.Sequential)]
unsafe struct Wvsamps
{
    const int Size = 3;
    fixed ushort wav1[Size];
    fixed ushort wav2[Size];
    fixed ushort wav3[Size];
    fixed ushort wav4[Size];
    public ushort modes;
    public uint time;
    public ushort chkSum;

    public ushort this[int wav, int index]
    {
        get
        {
            if (index<0||index>=Size)
            {
                throw new IndexOutOfRangeException("index");
            }
            switch (wav)
            {
                case 0:
                    fixed (ushort* ptr = wav1) { return ptr[index]; }
                case 1:
                    fixed (ushort* ptr = wav2) { return ptr[index]; }
                case 2:
                    fixed (ushort* ptr = wav3) { return ptr[index]; }
                case 3:
                    fixed (ushort* ptr = wav4) { return ptr[index]; }
                default:
                    throw new IndexOutOfRangeException("wav");
            }
        }
        set
        {
            if (index<0||index>=Size)
            {
                throw new IndexOutOfRangeException("index");
            }
            switch (wav)
            {
                case 0:
                    fixed (ushort* ptr = wav1) { ptr[index]=value; }
                    break;
                case 1:                                    
                    fixed (ushort* ptr = wav2) { ptr[index]=value; }
                    break;
                case 2:                                    
                    fixed (ushort* ptr = wav3) { ptr[index]=value; }
                    break;
                case 3:                                    
                    fixed (ushort* ptr = wav4) { ptr[index]=value; }
                    break;
                default:
                    throw new IndexOutOfRangeException("wav");
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Wvsamps[] ldSamps = new Wvsamps[0x800000];            

        for (int i = 0; i<ldSamps.Length; i++)
        {
            Wvsamps sample=new Wvsamps();

            sample.time=(uint)i;
            sample.modes=1;
            //wav1 = [1,2,3]
            sample[0, 0]=1;
            sample[0, 1]=2;
            sample[0, 2]=3;
            //wav2 = [4,5,6]
            sample[1, 0]=4;
            sample[1, 1]=5;
            sample[1, 2]=6;
            //wav3 = [7,8,9]
            sample[2, 0]=7;
            sample[2, 1]=8;
            sample[2, 2]=9;
            //wav4 = [10,11,12]
            sample[3, 0]=10;
            sample[3, 1]=11;
            sample[3, 2]=12;

            // VERY IMPORTANT
            // Structs needs to be assigned with one statement as a whole
            ldSamps[i]=sample;

            // You cannot do this, ldSamps[i].time = 10
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This code makes sense to me as it has a more familiar feel. I'm thinking I can set things up as you've shown and then where you have the for (; ; ) loop in Main I could use an event, set sample(s), modes and time to the values I acquire then set ldSamps[some index]=sample. In this way I'm thinking I'll be able to set 1 struct at a time to the acquired values, maintain an index to the structs in the ldSamps array and access the sample values for plotting and measurement. Does this sound correct or am I missing anything?
1

A simpler approach might be, not to use arrays at all inside the structures, but to declare the corresponding variables (wav11,wav12,wav13,wav21...).

[StructLayout(LayoutKind.Sequential)]
[ImmutableObject(true)]
public struct Wvsamps
{
    const int Size = 3;
    public readonly ushort wav11, wav12, wav13;
    public readonly ushort wav21, wav22, wav23;
    public readonly ushort wav31, wav32, wav33;
    public readonly ushort wav41, wav42, wav43;
    public readonly ushort modes;
    public readonly uint time;
    public readonly ushort chkSum;

    public Wvsamps(uint time, ushort modes,
        ushort wav11, ushort wav12, ushort wav13,
        ushort wav21, ushort wav22, ushort wav23,
        ushort wav31, ushort wav32, ushort wav33,
        ushort wav41, ushort wav42, ushort wav43,
        ushort chkSum)
    {
        this.time=time;
        this.modes=modes;
        this.wav11=wav11;
        this.wav12=wav12;
        this.wav13=wav13;
        this.wav21=wav21;
        this.wav22=wav22;
        this.wav23=wav23;
        this.wav31=wav31;
        this.wav32=wav32;
        this.wav33=wav33;
        this.wav41=wav41;
        this.wav42=wav42;
        this.wav43=wav43;
        this.chkSum=chkSum;
    }

}

public class Samples
{
    public const int Size = 0x800000;

    readonly Wvsamps[] sample1, sample2, sample3;

    public Samples()
    {
        sample1=new Wvsamps[Size];
        sample2=new Wvsamps[Size];
        sample3=new Wvsamps[Size];
    }

    public Wvsamps[] Sample1 { get { return sample1; } }
    public Wvsamps[] Sample2 { get { return sample2; } }
    public Wvsamps[] Sample3 { get { return sample3; } }
}


class Program
{
    static void Main(string[] args)
    {
        Samples data = new Samples();

        for (int i = 0; i<Samples.Size; i++)
        {
            data.Sample1[i]=new Wvsamps((uint)i, 1,
                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
                78);
            // Similarly data.Sample2[i]= ... 
        }
    }
}

3 Comments

I was pondering this, it just seemed less elegant as in C++ it was easy to index through the array of Wvsamps structs and the individual wav arrays within the structs using loops and pointers. Might be a good way to get along with C# though.
You already have the arrays split up in wav1, wav2, .. etc. I really depends on what information you get at what time because what you want is to initialize a struct at one time with readonly values and not write to it later. I suggest you edit the question to show by example what information would you get every X seconds in order to best determine how to log it in memory.
I updated my original post to include more information. Ideally the entire ldSamps array of Wvsamps structs is initialized to 0. As I receive 32byte network packets at 1/sec I need to write them into a Wvsamps struct then index ldSamps to point to the next Wvsamps struct so I can write the next 32byte packet there. I'd like to accumulate 0x40000 worth of Wvsamps structs this way and be able to retrieve the samples for waveform plotting and measurement.
0

There is a way you can try, using unsafe buffers, but never tried that. Allow unsafe code is required for it. (project setting)

unsafe struct Wvsamps
{
    fixed ushort wav1[3];
    fixed ushort wav2[3];
    fixed ushort wav3[3];
    fixed ushort wav4[3];
    ushort modes;
    uint time;
    ushort chkSum;
}       

11 Comments

Ok I tried using unsafe and set Project Properties - Build to Allow unsafe code but I still get the same compiler error.
@DaleH.you would need to change your struct definition to the same as the answer.
The struct should be marked as unsafe also. Like @dale said.
Ok, I changed the struct def to use static ushort[] wav1 = new ushort[3]; and it now compiles, however I can't access the elements from my Program class.
If you dont get this to work, making it static is the last thing i would suggest. Its prone to errors/multithreading
|
0

Here is a solution using native arrays, which is slower because four new arrays are created for each instance, and the contents of the arrays are scattered around the RAM. Modern processors work fastest when memory is as localized as possible.

[StructLayout(LayoutKind.Sequential)]    
struct Wvsamps
{
    const int Size = 3;
    public readonly ushort[] wav1;
    public readonly ushort[] wav2;
    public readonly ushort[] wav3;
    public readonly ushort[] wav4;
    public readonly ushort modes;
    public readonly uint time;
    ushort chkSum;

    public Wvsamps(uint time, ushort modes)
    {
        this.time=time;
        this.modes=modes;
        this.wav1=new ushort[Size];
        this.wav2=new ushort[Size];
        this.wav3=new ushort[Size];
        this.wav4=new ushort[Size];
        this.chkSum=0;
    }

    public void CalcChecksum()
    {
        this.chkSum=0;
        for (int i = 0; i<Size; i++)
        {
            chkSum=(ushort)((chkSum+wav1[i]+wav2[i]+wav3[i]+wav4[i])%256);
        }
    }

    public ushort Checksum { get { return chkSum; } }
}

class Program
{
    static void Main(string[] args)
    {
        Wvsamps[] ldSamps = new Wvsamps[0x800000];            

        for (int i = 0; i<ldSamps.Length; i++)
        {
            // The constructor initializes the arrays also
            Wvsamps sample=new Wvsamps( (uint)i, 1);

            //wav1 = [1,2,3]
            sample.wav1[0]=1;
            sample.wav1[1]=2;
            sample.wav1[2]=3;
            //wav2 = [4,5,6]
            sample.wav2[0]=4;
            sample.wav2[1]=5;
            sample.wav2[2]=6;
            //wav3 = [7,8,9]
            sample.wav3[0]=7;
            sample.wav3[1]=8;
            sample.wav3[2]=9;
            //wav4 = [10,11,12]
            sample.wav4[0]=10;
            sample.wav4[1]=11;
            sample.wav4[2]=12;

            sample.CalcChecksum();

            // VERY IMPORTANT
            // Structs needs to be assigned with one statement as a whole
            ldSamps[i]=sample;

            // You cannot do this: ldSamps[i].time = 10;
        }

        var checksum = ldSamps[1000].Checksum;
    }
}

Edit 1

By removing the readonly in the array definitions you can assign the values with one statement. Again, it depends on what you want to do, in terms of pre-allocating the arrays or not.

[StructLayout(LayoutKind.Sequential)]
struct Wvsamps
{
    const int Size = 3;
    public ushort[] wav1;
    public ushort[] wav2;
    public ushort[] wav3;
    public ushort[] wav4;
    public readonly ushort modes;
    public readonly uint time;
    ushort chkSum;

    public Wvsamps(uint time, ushort modes)
    {
        this.time=time;
        this.modes=modes;
        this.wav1=null;
        this.wav2=null;
        this.wav3=null;
        this.wav4=null;
        this.chkSum=0;
    }

and

        for (int i = 0; i<ldSamps.Length; i++)
        {
            Wvsamps sample = new Wvsamps((uint)i, 1);
            sample.wav1=new ushort[] { 1, 2, 3 };
            sample.wav2=new ushort[] { 4, 5, 6 };
            sample.wav3=new ushort[] { 7, 8, 9 };
            sample.wav4=new ushort[] { 10, 11, 12 };

        }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.