Skip to content

Instantly share code, notes, and snippets.

@Timoses
Created Apr 19, 2018
Embed
What would you like to do?
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