Jump to page: 1 2
Thread overview
Templates, D way
Sep 05, 2017
Void-995
Sep 05, 2017
John Colvin
Sep 05, 2017
crimaniak
Sep 05, 2017
Computermatronic
Sep 05, 2017
crimaniak
Sep 05, 2017
John Colvin
Sep 05, 2017
crimaniak
Sep 08, 2017
Corey Lubin
Sep 08, 2017
Corey Lubin
Sep 05, 2017
Void-995
Sep 05, 2017
Computermatronic
Sep 05, 2017
crimaniak
Sep 05, 2017
Void-995
Sep 05, 2017
crimaniak
Sep 05, 2017
Void-995
Sep 06, 2017
crimaniak
September 05, 2017
Hi, everyone. I'm pretty new to D and trying my first steps in it. Currently I'm trying to port some code from C/C++ with pretty weird data structures and don't like idea of making boilerplate functions for accessing sub-lists in main binary structure (lets not talk about it's design, it's from legacy thing I want to deal with and I can't change the format itself or writing a lot of additional parsing code). With hour I spent on trying I've ended with what you may see below, but I wonder if there is more pleasant variant of re-writing that template as I haven't found anything better in either books or online documentation yet:

template DataList(const char[] listName, T, alias dataOffset, alias listLength)
{
	const char[] DataList = format(q{
		%s[] %s()
		{
			return (cast(%s *)(cast(byte *)(&this) + %s))[0 .. %s];
		}
	}, T.stringof, listName, T.stringof, dataOffset.stringof, listLength.stringof);
}

struct MyBinarySubStructAForA
{
	int someIntegerFieldA;
	float someFloatFieldA;
}

struct MyBinarySubStructBForA
{
	int someIntegerFieldB;
	float someFloatFieldB;
}

struct MyBinaryStructA
{
	int firstSublistMembersCount;
	int firstSublistMembersOffset;

	int secondSublistMembersCount;
	int secondSublistMembersOffset;
	
	@property mixin(DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset));
	@property mixin(DataList!("secondSublist", MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset));
}

...

MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;
September 05, 2017
On Tuesday, 5 September 2017 at 11:08:57 UTC, Void-995 wrote:
> Hi, everyone. I'm pretty new to D and trying my first steps in it. Currently I'm trying to port some code from C/C++ with pretty weird data structures and don't like idea of making boilerplate functions for accessing sub-lists in main binary structure (lets not talk about it's design, it's from legacy thing I want to deal with and I can't change the format itself or writing a lot of additional parsing code). With hour I spent on trying I've ended with what you may see below, but I wonder if there is more pleasant variant of re-writing that template as I haven't found anything better in either books or online documentation yet:
>
> template DataList(const char[] listName, T, alias dataOffset, alias listLength)
> {
> 	const char[] DataList = format(q{
> 		%s[] %s()
> 		{
> 			return (cast(%s *)(cast(byte *)(&this) + %s))[0 .. %s];
> 		}
> 	}, T.stringof, listName, T.stringof, dataOffset.stringof, listLength.stringof);
> }
>
> struct MyBinarySubStructAForA
> {
> 	int someIntegerFieldA;
> 	float someFloatFieldA;
> }
>
> struct MyBinarySubStructBForA
> {
> 	int someIntegerFieldB;
> 	float someFloatFieldB;
> }
>
> struct MyBinaryStructA
> {
> 	int firstSublistMembersCount;
> 	int firstSublistMembersOffset;
>
> 	int secondSublistMembersCount;
> 	int secondSublistMembersOffset;
> 	
> 	@property mixin(DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset));
> 	@property mixin(DataList!("secondSublist", MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset));
> }
>
> ...
>
> MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;

mixin template DataList(string listName, T, alias dataOffset, alias listLength)
{
	mixin(
		q{T[] } ~ listName ~ q{() @property
		{
			return (cast(T*)(cast(ubyte*)&this + dataOffset))[0 .. listLength];
		}}
	);
}

struct MyBinarySubStructAForA
{
	int someIntegerFieldA;
	float someFloatFieldA;
}

struct MyBinarySubStructBForA
{
	int someIntegerFieldB;
	float someFloatFieldB;
}

struct MyBinaryStructA
{
	int firstSublistMembersCount;
	int firstSublistMembersOffset;

	int secondSublistMembersCount;
	int secondSublistMembersOffset;
	
	mixin DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset);
	mixin DataList!("secondSublist", MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset);
}
September 05, 2017
On Tuesday, 5 September 2017 at 11:08:57 UTC, Void-995 wrote:
> 	@property mixin(DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset));
 I don't think string mixins are required here. It seems just template is more simple.

T[] getBytesAs(T, alias length, alias offset)()
{
    return (cast(T *)(cast(byte *)(&this) + offset))[0 .. length];
}

struct MyBinaryStructA
{
 ...
    alias firstList = getBytesAs!(MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset);
    alias secondList = getBytesAs!(MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset);
}

unittest
{
...
    MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;

    auto a = binaryData.firstList;
}
September 05, 2017
On Tuesday, 5 September 2017 at 12:20:14 UTC, crimaniak wrote:
> On Tuesday, 5 September 2017 at 11:08:57 UTC, Void-995 wrote:
>> 	@property mixin(DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset));
>  I don't think string mixins are required here. It seems just template is more simple.
>
> T[] getBytesAs(T, alias length, alias offset)()
> {
>     return (cast(T *)(cast(byte *)(&this) + offset))[0 .. length];
> }
>
> struct MyBinaryStructA
> {
>  ...
>     alias firstList = getBytesAs!(MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset);
>     alias secondList = getBytesAs!(MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset);
> }
>
> unittest
> {
> ...
>     MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;
>
>     auto a = binaryData.firstList;
> }

I find it very strange that this works, as a non-mixin template should not be able to capture the context of where it was instantiated. If you take the alias template parameters out it behaves how it should (that is an error message saying this is not accessible).
September 05, 2017
On Tuesday, 5 September 2017 at 12:20:14 UTC, crimaniak wrote:
> On Tuesday, 5 September 2017 at 11:08:57 UTC, Void-995 wrote:
>> 	@property mixin(DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset));
>  I don't think string mixins are required here. It seems just template is more simple.
>
> T[] getBytesAs(T, alias length, alias offset)()
> {
>     return (cast(T *)(cast(byte *)(&this) + offset))[0 .. length];
> }
>
> struct MyBinaryStructA
> {
>  ...
>     alias firstList = getBytesAs!(MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset);
>     alias secondList = getBytesAs!(MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset);
> }
>
> unittest
> {
> ...
>     MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;
>
>     auto a = binaryData.firstList;
> }

Thanks, that definitely working and doesn't require mixin with strings. But while waiting for response I've tried another thing, and I doubt I would able do to that without string now:

template MyBinaryStructGenericDataList(string listName, alias Type)
{
	import std.string;

	const char[] MyBinaryStructGenericDataList = format(q{
		int %sCount;
		int %sOffset;

		@property %s[] %s() const
		{
			return (cast(%s *)(cast(ubyte *)(&this) + %sOffset))[0 .. %sCount];
		}
	}, listName, listName, Type.stringof, listName, Type.stringof, listName, listName);
}

struct MyBinarySubStructAForA
{
	int someIntegerFieldA;
	float someFloatFieldA;
}

struct MyBinarySubStructBForA
{
	int someIntegerFieldB;
	float someFloatFieldB;
}

struct MyBinaryStructA
{
	mixin(MyBinaryStructGenericDataList!("firstSublist", MyBinarySubStructAForA));
	mixin(MyBinaryStructGenericDataList!("secondSublist", MyBinarySubStructBForA));
}

...

MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;

...
September 05, 2017
On Tuesday, 5 September 2017 at 12:54:20 UTC, Void-995 wrote:
> On Tuesday, 5 September 2017 at 12:20:14 UTC, crimaniak wrote:
>> On Tuesday, 5 September 2017 at 11:08:57 UTC, Void-995 wrote:
>>> 	@property mixin(DataList!("firstSublist", MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset));
>>  I don't think string mixins are required here. It seems just template is more simple.
>>
>> T[] getBytesAs(T, alias length, alias offset)()
>> {
>>     return (cast(T *)(cast(byte *)(&this) + offset))[0 .. length];
>> }
>>
>> struct MyBinaryStructA
>> {
>>  ...
>>     alias firstList = getBytesAs!(MyBinarySubStructAForA, firstSublistMembersCount, firstSublistMembersOffset);
>>     alias secondList = getBytesAs!(MyBinarySubStructBForA, secondSublistMembersCount, secondSublistMembersOffset);
>> }
>>
>> unittest
>> {
>> ...
>>     MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;
>>
>>     auto a = binaryData.firstList;
>> }
>
> Thanks, that definitely working and doesn't require mixin with strings. But while waiting for response I've tried another thing, and I doubt I would able do to that without string now:
>
> template MyBinaryStructGenericDataList(string listName, alias Type)
> {
> 	import std.string;
>
> 	const char[] MyBinaryStructGenericDataList = format(q{
> 		int %sCount;
> 		int %sOffset;
>
> 		@property %s[] %s() const
> 		{
> 			return (cast(%s *)(cast(ubyte *)(&this) + %sOffset))[0 .. %sCount];
> 		}
> 	}, listName, listName, Type.stringof, listName, Type.stringof, listName, listName);
> }
>
> struct MyBinarySubStructAForA
> {
> 	int someIntegerFieldA;
> 	float someFloatFieldA;
> }
>
> struct MyBinarySubStructBForA
> {
> 	int someIntegerFieldB;
> 	float someFloatFieldB;
> }
>
> struct MyBinaryStructA
> {
> 	mixin(MyBinaryStructGenericDataList!("firstSublist", MyBinarySubStructAForA));
> 	mixin(MyBinaryStructGenericDataList!("secondSublist", MyBinarySubStructBForA));
> }
>
> ...
>
> MyBinaryStructA *binaryData = cast(MyBinaryStructA *)fileData.ptr;
>
> ...

Yes, there is definately no other way but to use string mixins here, however if you switch to a mixin template, and put the string mixin inside of that, you don't have to do text replacement to use the template parameters.

Example:
mixin template GenericDataList(string name, T)
{
    mixin(`
        int `~name~`SublistCount;
        int `~name~`SublistOffset;

        @property auto `~name~`Sublist()
        {
            return (cast(T*)(cast(byte*)&this + `~name~`SublistOffset))[0..`~name~`SublistCount];
        }`);
}

struct MyBinarySubStructAForA
{
    int someIntegerFieldA;
    float someFloatFieldA;
}

struct MyBinarySubStructBForA
{
    int someIntegerFieldB;
    float someFloatFieldB;
}

struct MyBinaryStructA
{
    mixin MyBinaryStructGenericDataList!("first", MyBinarySubStructAForA);
    mixin MyBinaryStructGenericDataList!("second", MyBinarySubStructBForA);
}
September 05, 2017
On Tuesday, 5 September 2017 at 12:41:45 UTC, Computermatronic wrote:
> I find it very strange that this works, ...
I'm too. :) In any case, it's not a big deal. It's possible to modify this template to transfer `this` as the first parameter and modify its usage accordingly.


September 05, 2017
On Tuesday, 5 September 2017 at 12:54:20 UTC, Void-995 wrote:
> Thanks, that definitely working and doesn't require mixin with strings. But while waiting for response I've tried another thing, and I doubt I would able do to that without string now:
>...
> 		int %sCount;
> 		int %sOffset;

Yes, you can make such custom names only with string mixin, as I know. Computermatronic gives a good example how to do it simple way. I can give two additional advises: as I understand offsets as sizes are fixed so think about using unions to access sub structures instead of these auto-generated methods; I have a long history of using tricks like this starting from CHAIN MERGE usage in GW-BASIC to add some functions and for now such things are definitely code smell for me. There is always a way to do the same, but better.

September 05, 2017
On Tuesday, 5 September 2017 at 12:41:45 UTC, Computermatronic wrote:
> On Tuesday, 5 September 2017 at 12:20:14 UTC, crimaniak wrote:
>> [...]
>
> I find it very strange that this works, as a non-mixin template should not be able to capture the context of where it was instantiated. If you take the alias template parameters out it behaves how it should (that is an error message saying this is not accessible).

https://issues.dlang.org/show_bug.cgi?id=17809
September 05, 2017
On Tuesday, 5 September 2017 at 14:01:02 UTC, crimaniak wrote:
> On Tuesday, 5 September 2017 at 12:54:20 UTC, Void-995 wrote:
>> Thanks, that definitely working and doesn't require mixin with strings. But while waiting for response I've tried another thing, and I doubt I would able do to that without string now:
>>...
>> 		int %sCount;
>> 		int %sOffset;
>
> Yes, you can make such custom names only with string mixin, as I know. Computermatronic gives a good example how to do it simple way. I can give two additional advises: as I understand offsets as sizes are fixed so think about using unions to access sub structures instead of these auto-generated methods; I have a long history of using tricks like this starting from CHAIN MERGE usage in GW-BASIC to add some functions and for now such things are definitely code smell for me. There is always a way to do the same, but better.

Using unions? <Type>Count and <Type>Offset are different depending on input data, so the address where they are is varying depending on which file I've loaded. Or I didn't get what you meant.
« First   ‹ Prev
1 2