dstep
dstep copied to clipboard
C bitfields
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.
Could you please provide a fully compilable example for the C code.
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));
}
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?
BTW, it's possible to syntax highlight code examples in GitHub [1].
[1] https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown
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 .
I believe this is padding.
Aha, ok.
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.
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.
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.
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));
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.
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.
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.
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.
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.
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.