dstep icon indicating copy to clipboard operation
dstep copied to clipboard

C bitfields

Open russel opened this issue 7 years ago • 16 comments

I guess bitfields are not yet handled by DStep: certainly the bitfields in libdvbv5 header files do not seem to map to anything looking like a bitfield in D.

The current problem I have though is that C allows anonymous entries in a bit field:

uint32_t :4;

which transforms to:

uint;

which is invalid D and so causes a compilation error.

russel avatar Jun 13 '17 13:06 russel

Could you please provide a fully compilable example for the C code.

jacob-carlborg avatar Jun 13 '17 19:06 jacob-carlborg

The C code:

struct bitfield {
	unsigned int one : 4;
	unsigned int two : 8;
	unsigned int : 4;
};

which is an entirely valid, and compilable, bitfield specification transforms to the D code:

extern (C):

struct bitfield
{
    uint one;
    uint two;
    uint ;
}

which is neither a bitfield nor compilable. :-(

As I understand things there is not C/C++ style bitfield specification in D, but that there is a mixin to achieve the goal. cf. http://dlang.org/phobos/std_bitmanip.html So I am guessing something like:

import std.bitmanip: bitfields;

struct bitfield {
    ushort a;
    mixin(bitfields!(
        uint, "one", 4,
        uint, "two", 8,
        uint, "madeUpName1", 4));
}

russel avatar Jun 14 '17 06:06 russel

As I understand things there is not C/C++ style bitfield specification in D, but that there is a mixin to achieve the goal.

Yes. Last time I checked (ages ago) std.bitmanip.bitfields didn't allow to enter arbitrary values. That is, there were some C headers containing values that did not compile using the same values with std.bitmanip.bitfields.

Out of curiosity, in C, how do you access the anonymous field/member, if possible?

jacob-carlborg avatar Jun 14 '17 06:06 jacob-carlborg

BTW, it's possible to syntax highlight code examples in GitHub [1].

[1] https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown

jacob-carlborg avatar Jun 14 '17 06:06 jacob-carlborg

Out of curiosity, in C, how do you access the anonymous field/member, if possible?

I believe this is padding.

On Jun 14, 2017 08:50, "jacob-carlborg" [email protected] wrote:

BTW, it's possible to syntax highlight code examples in GitHub [1].

[1] https://guides.github.com/features/mastering-markdown/# GitHub-flavored-markdown

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/jacob-carlborg/dstep/issues/151#issuecomment-308336025, or mute the thread https://github.com/notifications/unsubscribe-auth/AHfoNL4var8u_Hzt09g1X00MsG5GBmANks5sD4KmgaJpZM4N4d0j .

ciechowoj avatar Jun 14 '17 06:06 ciechowoj

I believe this is padding.

Aha, ok.

jacob-carlborg avatar Jun 14 '17 07:06 jacob-carlborg

On my x86_64 machine GCC always seems to compile bitfields to 32-bit, so I was wrong to put ushort in the D example, I think it should be uint for this architecture.

russel avatar Jun 14 '17 07:06 russel

The padding is required because some memory mapped hardware control words have really weird structures with useful bits scattered through the word, and some bits unused. Rather than force people to construct useless names C, and C++, allow anonymous fields for the unused bits.

russel avatar Jun 14 '17 07:06 russel

I added a couple of main functions to try and compare usage. So in C:

int main(const int argc, const char *const *const argv) {
	struct bitfield x = {1, 2};
	printf("%d, %d, %d\n", x.one, x.two, sizeof(x));
	x.one = 4;
	x.two = 6;
	printf("%d, %d, %d\n", x.one, x.two, sizeof(x));
	return 0;
}

whilst in D:

int main(string[] args) {
	bitfield x = {1, 2}; // Doesn't fail but assigns field one with 2, and two with 0.
	writefln("%d, %d, %d", x.one, x.two, x.sizeof);
	x.one = 4;
	x.two = 6;
	writefln("%d, %d, %d", x.one, x.two, x.sizeof);
	return 0;
}

Of course this is irrelevant for DStep since it is not interested in function bodies.

russel avatar Jun 14 '17 07:06 russel

bitfield x = {1, 2}; // Doesn't fail but assigns field one with 2, and two with 0.

What's happening here is that {1, 2} is a struct initializer, which will only initialize fields declared in the struct. What std.bitmanip.bitfields is doing is generating one field for the data and two methods for each specified field (one getter and one setter).

When you use the struct initializer {1, 2}, it will assign 1 to the a field that you have declared in the struct and 2 to the field that bitfields is generating. In this case the field is called _one_two_madeUpName1. You can look at the code that bitfields is generating by calling the template in a pragma(msg) call:

pragma(msg, bitfields!(
    uint, "one", 4,
    uint, "two", 8,
    uint, "madeUpName1", 4));

jacob-carlborg avatar Jun 14 '17 07:06 jacob-carlborg

But the struct has only one member a, there should be no other members. If there is then D "bitfields" cannot do what C and C++ bitfields are for. The std.bitfield.bitfields template should only be introducing functions to read and write via bit operations the member a. If the template is adding members then there is a real problem with it, making it unusable to handle C and C++ bitfields.

Clearly trying to initialise the D bitfield instance using struct initialisation has to fail, because D does not support bitfields. However the std.bitfield.bitfields template should provide an initialiser, which it appears not to, which is a severe disappointment.

russel avatar Jun 14 '17 07:06 russel

But the struct has only one member a, there should be no other members. If there is then D "bitfields" cannot do what C and C++ bitfields are for. The std.bitfield.bitfields template should only be introducing functions to read and write via bit operations the member a. If the template is adding members then there is a real problem with it, making it unusable to handle C and C++ bitfields.

It's automatically adding a member. It needs to have somewhere to store the bits. I would guess it picks the correct type/size for the member depending on how many bits that needs to be stored.

Clearly trying to initialise the D bitfield instance using struct initialisation has to fail, because D does not support bitfields.

I'm not sure if that's possible.

However the std.bitfield.bitfields template should provide an initialiser, which it appears not to, which is a severe disappointment.

It can provide a constructor, but the syntax you used above is a struct initializer and cannot be overridden.

jacob-carlborg avatar Jun 14 '17 11:06 jacob-carlborg

Understood that:

bitfield x = {1, 2};

cannot be supported, but I also tried:

bitfield x = bitfield(1, 2);

which gave the same result. I am very close to constructing a big report for this one; well more of a breaking change request.

russel avatar Jun 14 '17 11:06 russel

bitfield x = bitfield(1, 2);

Yes, the compiler will generate a constructor automatically, with one parameter for each field and all parameters are optional. For example:

struct Foo
{
    int a;
    int b;
}

Will generate:

struct Foo
{
    int a;
    int b;

    this(int a = int.init, int b = int.init)
    {
        this.a = a;
        this.b = b;
    }
}

I checked and there's a couple of options. If you manually define a constructor in a struct, you cannot use the struct initializer but you can still default initialize the struct:

struct Foo
{
    int a;

    this (int a)
    {
        this.a = a;
    }
}

void main()
{
    Foo bar; // ok
    Foo foo =  { 3 }; // Error: struct Foo has constructors, cannot use { initializers }
}

If you disable the default constructor for a struct, the compiler will force you to define a constructor, but then default initialization won't work:

struct Foo
{
    int a;
    @disable this();

    this (int a)
    {
        this.a = a;
    }
}

void main()
{
    Foo bar; // Error: variable main.main.bar default construction is disabled for type Foo
    Foo foo = Foo(3); // ok
}

So it depends on what your needs are.

jacob-carlborg avatar Jun 14 '17 13:06 jacob-carlborg

That all seems fine and reasonable, but what the bitfields mixin should do is disable the default initialiser and have one that initialises each field by using the appropriate bit twiddling.

auto x = bitfield(2, 4);

should set x.one to 2 and x.two to 4 rather than setting a to 4. But I think this is a std.bitfield problem not a DStep one.

russel avatar Jun 14 '17 13:06 russel

Disabling the default initializer seems a bit too aggressive to me. But perhaps generating a constructor automatically and let the user decide if the default initializer should be disabled or not.

jacob-carlborg avatar Jun 14 '17 14:06 jacob-carlborg