7
votes

Is there any workaround for the error CS1663 ("Fixed size buffer type must be one of the following: bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float or double.")?

I need to declare a unsafe fixed array from another blittable custom type struct but I'm stuck in this compiler error.

Showing some code to elucidate the problem below.

struct s1
{
    byte _b1;
    byte _b2;
}

unsafe struct s2
{
    fixed s1 _s1[5]; // CS1663 here...
}

Note that the two structs are blittable, so the error doesn't make any sense for me.

Anyone have any idea about what I could do?

Thanks.

4
Do you mean unsafe struct s2 { fixed s1 s1[5]; }?Vadim Martynov
Yes!!! I will fix this little error in the code snippet, thanks. Any thoughts on this @VadimMartynov?andresantacruz
Well, you just have a syntax error. You should to declare field modifiers (like private, fixed, readonly, static and other), field type (like byte, int, s1, string) and field name (any name that will be used to reffer your varable. You are missing field type (s1). Read more about declaring fixed buffers: msdn.microsoft.com/en-us/library/zycewsya(v=vs.80).aspxVadim Martynov
@VadimMartynov I know I have a syntax error :D My question revolves around the "why" I'm getting this syntax error. The only reason for this error I can imagine is to avoid fixing in memory a non-blittable type. The point is that my type is actually blittable. There have to be some workaround for this limitation, this should be a very common problem.andresantacruz

4 Answers

1
votes

It's a restriction of fixed size buffers.

The fixed array can take any of the attributes or modifiers that are allowed for regular struct members. The only restriction is that the array type must be bool, byte, char, short, int, long, sbyte, ushort, uint, ulong, float, or double.

You can use only that types but not combination (like struct contains only that types). There is no difference if your type bittable or not. You just can't use it.

Then you can't use your custom struct for the fixed size buffer.

Workaround? Mmmm, yes, may be. You can change your code structure and use something like this:

unsafe struct s2
{
    fixed byte _b1[5]; 
    fixed byte _b2[5]; 
}
1
votes

Another workaround is to either use pointer type fields.

public struct Foo
{
    public byte _b1;
    public byte _b2;
}

public unsafe struct Bar
{
    public int Length;
    public unsafe Foo* Data;
}

Alternately, if your intention was to make a single value type object which contains all of its own data, contiguously (ie, so it can be memcpy'd in unmanaged code) -- then the only way to do that is by not using any arrays at all.

public struct Foo
{
    public byte _b1;
    public byte _b2;
}

public struct Bar1
{
    public Foo F1;
}

public struct Bar2
{
    public Foo F1;
    public Foo F2;
}

public struct Bar3
{
    public Foo F1;
    public Foo F2;
    public Foo F3;
}
0
votes

How I would approach your example and similar complexity ones would be to first ask myself if the struct can be reduced to a single primitive type. If it can then I would merely hide the fixed buffer inside the struct via property or indexer access.

In your example you had the following.

struct s1
{
    byte _b1;
    byte _b2;
}

unsafe struct s2
{
    fixed s1 _s1[5]; // CS1663 here...
}

What I might consider if I decided I absolutely needed the structs to line up in a single contiguous block of data is something like this.

[StructLayout(LayoutKind.Explicit, Size = 2)]
struct s1
{   // Field offsets to emulate union style access which makes it
    // simple to get at the raw data in a primitive type format.

    [FieldOffset(0)]ushort _u1;
    [FieldOffset(0)]byte _b1;
    [FieldOffset(1)]byte _b2;

    public s1(ushort data)
    {
        _b1 = 0;
        _b2 = 0;
        _u1 = data;
    }

    public ushort ToUShort()
    {
        return _u1;
    }
}

unsafe struct s2
{
    public const int Size = 5;
    private fixed ushort _s1[Size];

    public s1 this[int index]
    {   // A public indexer that provides the data in a friendlier format.
        get
        {
            if (index < 0 || index >= Size )
                throw new IndexOutOfRangeException();
            return new s1(_s1[index]);
        }
        set
        {
            if (index < 0 || index >= Size)
                throw new IndexOutOfRangeException();
            _s1[index] = value.ToUShort();
        }
    }
}

If this looks like a hack, that's because it kinda is. I wouldn't recommend this as a general solution because it's difficult to maintain but in those rare instances where you're working at this sort of low level and know in advance that the data spec won't be changing, then something like this technique can be viable. But, even in those cases I'd still prefer to encapsulate as much of this low level stuff as is possible to minimize the chances of something going wrong. That said, this does do exactly as asked which is for a workaround to having a fixed sized buffer of custom structs.

0
votes
[StructLayout(LayoutKind.Sequential)]
public struct S1
{
    [MarshalAs(UnmanagedType.I1)] public byte _b1; 
    [MarshalAs(UnmanagedType.I1)] public byte _b2; 

    public S1(ushort _ushort)
    {
        this = new Converter(_ushort).s1;
    }
    public ushort USHORT() //for fixed arrays
    {
        return new Converter(this).USHORT;
    }

    [StructLayout(LayoutKind.Explicit)]
    private struct Converter
    {
        [FieldOffset(0)] public S1 s1;
        [FieldOffset(0)] public ushort USHORT;

        public Converter(S1 _s1)
        {
            USHORT = 0;
            s1 = _s1;
        }

        public Converter(ushort _USHORT)
        {
            s1 = default;
            USHORT = _USHORT;
        }
    }
}

public unsafe struct S2
{
    public fixed ushort s1[5];

    public S1 this[int n] {
        get => new S1(s1[n]); //copy!
        set => s1[n] = value.USHORT();
    }
}