Created
April 19, 2018 15:07
-
-
Save Timoses/c78e599e91b8d05be34aefaf75ca3739 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module parameter; | |
import std.stdio; | |
import std.algorithm; | |
import std.traits; | |
import std.meta; | |
import std.conv; | |
interface IParameter | |
{ | |
void toString(scope void delegate(const(char)[]) sink) const; | |
@property string[] members(); | |
@property size_t length(); | |
IParameter opIndex(size_t i); | |
IParameter opIndex(string memberName); | |
/* Pointer to Parameter */ | |
@property void* ptr(); | |
/* Byte size of Parameter */ | |
@property size_t byteSize(); | |
T to(T)() | |
{ | |
assert(byteSize == T.sizeof); | |
return *cast(T*)(ptr); | |
} | |
} | |
template Parameter(T) | |
{ | |
static if (isArray!T && !is(T : string)) | |
alias TypeNonArray = ArrayElementType!T; | |
else | |
alias TypeNonArray = T; | |
// Find members | |
static if (!isBasicType!T && !isArray!T) | |
{ | |
// Find all getter functions | |
template AllMemberFunctions(T) | |
{ | |
template createDg(alias fn) | |
{ | |
// skip bitfield setters | |
static if (Parameters!fn.length > 0) | |
alias createDg = AliasSeq!(); | |
else static if (__traits(isStaticFunction, fn)) | |
alias createDg = fn; | |
else | |
ReturnType!fn createDg(ref T ctx, Parameters!fn args) | |
{ | |
ReturnType!fn delegate(Parameters!fn) fun; | |
fun.funcptr = &fn; | |
fun.ptr = cast(void*)&ctx; | |
return fun(args); | |
} | |
} | |
template GetOverloads(string name) | |
{ | |
static if (is(typeof(__traits(getOverloads, T, name)))) | |
{ | |
alias GetOverloads = AliasSeq!(__traits(getOverloads, T, name)); | |
} | |
else | |
alias GetOverloads = AliasSeq!(); | |
} | |
alias AllMemberFunctions = staticMap!(createDg, | |
staticMap!(GetOverloads, __traits(allMembers, T))); | |
} | |
// Create a map which maps 'Unfolded' to AllFields or Funcs/funcNames | |
// with j = MemberMap[i] where 0 <= i < Unfolded.length | |
// then | |
// if j >= AllFields.length | |
// then member: Funcs[j - AllFields.length] | |
// else member: AllFields[j] | |
template GetMemberMap(int i, alias fields, string[] funcNames, | |
string[] AllFields) | |
{ | |
template GetIndex(alias field) | |
{ | |
import std.algorithm : countUntil; | |
static if (funcNames.canFind(field)) | |
alias GetIndex = Alias!( | |
AllFields.length + funcNames.countUntil(field)); | |
else static if (AllFields.canFind(field)) | |
alias GetIndex = Alias!( | |
AllFields.countUntil(field)); | |
else | |
// alias GetIndex = Alias!(i); | |
static assert(false); | |
} | |
static if (fields.length > 1) | |
alias GetMemberMap = AliasSeq!( | |
GetIndex!(fields[0]), | |
GetMemberMap!(i+1, fields[1..$], | |
funcNames, AllFields)); | |
else | |
alias GetMemberMap = GetIndex!(fields[0]); | |
} | |
alias AllFields = FieldNameTuple!T; | |
alias _allMemberNames = aliasSeqOf!([__traits(allMembers, T)]); | |
alias _funcMembers = Filter!(isFunction, staticMap!(GetMember, _allMemberNames)); | |
alias _funcNames = staticMap!(GetMemberName, _funcMembers); | |
alias Funcs = AllMemberFunctions!T; | |
alias Unfolded = UnfoldMembers!T; | |
alias MemberMap = GetMemberMap!(0, [Unfolded], | |
[_funcNames], [AllFields]); | |
} | |
else | |
{ | |
alias Unfolded = AliasSeq!(); | |
} | |
class Parameter : IParameter | |
{ | |
private: | |
T* _mtype; | |
public: | |
this(ref ubyte[] stream) | |
{ | |
// TODO: some types have variable length "(<min>[:<max>])" | |
// -> see how long stream is and check how often (N) type T | |
// fits into stream?! | |
static if (isArray!T) | |
// because array should point to array | |
this._mtype = cast(T*)(&stream); | |
else | |
// because T pointer should point to actual data of array | |
this._mtype = cast(T*)(stream.ptr); | |
} | |
this(T inValue) | |
{ | |
import core.memory : pureMalloc; | |
this._mtype = cast(T*)pureMalloc(T.sizeof); | |
import core.stdc.string : memcpy; | |
memcpy(this._mtype, &inValue, T.sizeof); | |
} | |
void toString(scope void delegate(const(char)[]) sink) const | |
{ | |
import std.conv : to; | |
sink((*(this._mtype)).to!string); | |
} | |
@property string[] members() | |
{ | |
static if (Unfolded.length == 0) | |
return []; | |
else | |
return [Unfolded]; | |
} | |
@property size_t length() | |
{ | |
static if (isArray!T && !is(T : string)) | |
{ | |
return this._mtype.length; | |
} | |
else | |
return Unfolded.length; | |
} | |
unittest | |
{ | |
byte[3] b = [0x01, 0x02, 0x03]; | |
auto t = new Parameter!(byte[3])(b); | |
assert(t.length == 3); | |
} | |
IParameter opIndex(size_t i) | |
{ | |
assert(i < this.length); | |
static if (!isBasicType!T && !isArray!T) | |
{ | |
switch (i) | |
{ | |
static foreach (index, trueIndex; MemberMap) | |
{ | |
case index: | |
{ | |
static if (MemberMap[index] >= AllFields.length) | |
{ | |
switch (trueIndex - AllFields.length) | |
{ | |
static foreach (j, fn; Funcs) | |
{ | |
case j: | |
return new Parameter! | |
(ReturnType!fn) | |
(fn(*(this._mtype))); | |
} | |
default: | |
return null; | |
} | |
} | |
else | |
return new Parameter! | |
(typeof(this._mtype.tupleof[MemberMap[index]])) | |
(this._mtype.tupleof[MemberMap[index]]); | |
} | |
} | |
default: | |
return null; | |
} | |
} | |
else static if (isArray!T) | |
return new Parameter!(typeof(this._mtype[i]))(this._mtype[i]); | |
else | |
return null; | |
} | |
IParameter opIndex(string memberName) | |
{ | |
static if (!isBasicType!T && !isArray!T) | |
{ | |
auto idx = [Unfolded].countUntil(memberName); | |
if (idx < 0) | |
// not found | |
return null; | |
else | |
return this[idx]; | |
} | |
else | |
return null; | |
} | |
@property void* ptr() { return cast(void*)(this._mtype); } | |
@property size_t byteSize() { return T.sizeof; } | |
T get() { return *(this._mtype); } | |
} | |
template GetMember(string memberName) | |
{ | |
// in case memberName does not exist or is private/protected | |
static if (is(typeof(__traits(getMember, T, memberName)))) | |
alias GetMember = Alias!(__traits(getMember, T, memberName)); | |
else | |
alias GetMember = Alias!null; | |
} | |
import std.string : split; | |
alias GetMemberName(alias member) = Alias!(fullyQualifiedName!(member).split('.')[$-1]); | |
} | |
unittest | |
{ | |
ubyte[] stream = [104, 105]; | |
string streamstring = cast(string)stream; | |
assert((new Parameter!(string)(stream)).get == streamstring); | |
assert((new Parameter!(string)(streamstring)).get == streamstring); | |
import std.bitmanip; | |
struct F | |
{ | |
uint one; | |
mixin(bitfields!(bool, "field1", 1, uint, "field2", 7)); | |
} | |
ubyte[] streami = nativeToLittleEndian(0x00000011) ~ cast(ubyte[])[0x05]; | |
F f = *cast(F*)(streami.ptr); | |
assert(streami.length == 5); | |
auto structType = new Parameter!F(streami); | |
assert(structType[0].to!uint == f.one); | |
assert(structType[1].to!bool == f.field1); | |
assert(structType[2].to!uint == f.field2); | |
assert(structType["one"].to!uint == f.one); | |
} | |
template ArrayElementType(T : T[]) | |
{ | |
alias T ArrayElementType; | |
} | |
// Returns all field members of struct T in order | |
// If T contains bitfields it returns the bitfield fields as well | |
template UnfoldMembers(T) | |
{ | |
import std.range : drop; | |
import std.string : split; | |
template GetMember(string memberName) | |
{ | |
// in case memberName does not exist or is private/protected | |
static if (is(typeof(__traits(getMember, T, memberName)))) | |
alias GetMember = Alias!(__traits(getMember, T, memberName)); | |
else | |
alias GetMember = Alias!null; | |
} | |
alias GetMemberName(alias member) = Alias!(fullyQualifiedName!(member).split('.')[$-1]); | |
// Checks if Field is a bitfield member. If so unfolds | |
// bitfield fields and returns them. Otherwise returns Field. | |
// Strategy: | |
// a bitfield member is named '_<field1>_<field2>_...'. Further, | |
// T contains functions named '<field1>', '<field2>' in the same | |
// order as they are listed in the bitfield member variable name. | |
// -> Iterate over function names, if found in bitfield member string | |
// add it to bitfieldMembers | |
// => If bitfieldMembers == bitfield member (all bitfields | |
// discovered) return them, otherwise return Field | |
template GetBitfieldMembers(alias Field) | |
{ | |
// Returns true if the function name can be found in Field | |
template discoverBitfieldMembers(string funcName) | |
{ | |
static if (Field.canFind(funcName)) | |
alias discoverBitfieldMembers = funcName; | |
else | |
alias discoverBitfieldMembers = Alias!""; | |
} | |
static if (Field[0] != '_') | |
{ | |
alias GetBitfieldMembers = Field; | |
} | |
else | |
{ | |
alias allMemberNames = aliasSeqOf!([__traits(allMembers, T)]); | |
alias funcMembers = Filter!(isFunction, staticMap!(GetMember, allMemberNames)); | |
alias funcMemberNames = staticMap!(GetMemberName, funcMembers); | |
alias bitfieldMembers = staticMap!(discoverBitfieldMembers, | |
funcMemberNames); | |
import std.array : join; | |
static if (bitfieldMembers.length > 0 && | |
"_" ~ [bitfieldMembers].join("_") == Field) | |
alias GetBitfieldMembers = bitfieldMembers; | |
else | |
alias GetBitfieldMembers = Field; | |
} | |
} | |
alias AllFields = FieldNameTuple!T; | |
alias UnfoldMembers = staticMap!(GetBitfieldMembers, AllFields); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment