Skip to content

Instantly share code, notes, and snippets.

@valmat
Last active June 1, 2020 18:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valmat/763c72465d7a1737229ae1c91393d629 to your computer and use it in GitHub Desktop.
Save valmat/763c72465d7a1737229ae1c91393d629 to your computer and use it in GitHub Desktop.
variable destructuring; array to variables list; destructuring assign. Traversable, Tuple
//
// May destructing variables for itarable types and tuples
//
module vlm.utils.destructing;
import std.range : empty, popFront, front;
import std.traits : isIterable;
import std.typecons : Tuple, tuple, isTuple;
import std.functional : forward;
auto tie(Args...)(ref Args args)
{
static assert(Args.length, "don't call with emprty args");
return TieInstance!(Args)(args);
}
/// Helper structure to collect pointers to arguments
private struct PointersSeq(size_t level, Arg0)
{
Arg0* item;
this(ref Arg0 arg0)
{
item = &arg0;
}
void set(size_t index)(ref Arg0 arg0)
if(level == index)
{
*item = arg0;
}
}
private struct PointersSeq(size_t level, Arg0, Args...)
{
Arg0* item;
PointersSeq!(level+1, Args) other;
this(ref Arg0 arg0, ref Args args)
{
item = &arg0;
other = args;
}
void set(size_t index)(auto ref Arg0 arg0)
if(level == index)
{
*item = arg0;
}
void set(size_t index, T)(auto ref T rhs)
if(level < index)
{
other.set!(index)(rhs);
}
}
private alias PointersSeq(Arg0, Args...) = PointersSeq!(0, Arg0, Args);
/// Helper template type. Usage on cast in TieInstance::applyTuple
/// Allows to get access to its arguments by index
private template args_indexed_t(size_t index, Arg0, Args...)
{
static assert(index < Args.length + 1);
static if(0 == index) {
alias args_indexed_t = Arg0;
} else {
alias args_indexed_t = Args[index-1];
}
}
/// Helper structure.
/// Due to the overload of assignment operators allows to use expression tie(x, y, ...) = ...
private struct TieInstance(Arg0, Args...)
{
PointersSeq!(Arg0, Args) items;
alias args_t(size_t index) = args_indexed_t!(index, Arg0, Args);
// In the constructor retrive arguments pointers
this(ref Arg0 arg0, ref Args args)
{
items = PointersSeq!(Arg0, Args)(arg0, args);
}
// Helper method to iterate at compile time
private void applyTuple(size_t index, T...)(auto ref Tuple!(T) rhs)
if(T.length > 0)
{
// Cast operator allows extends argument types
// for example, you can write:
// size_t a;
// tie(a) = tuple(5);
// because 5 is int, without cast it leads to compile error
items.set!index( cast(args_t!index) rhs[index] );
// At the same time iterate over index and tuple arguments
// (at compile time)
static if(index < T.length - 1 && index < Args.length) {
applyTuple!(index+1, T)(forward!rhs);
}
static assert(index < T.length);
}
// overloading assignment operator (empty Tuple)
void opAssign(Tuple!() rhs) {}
// overloading assignment operator
void opAssign(T...)(auto ref Tuple!(T) rhs)
{
applyTuple!(0, T)( forward!rhs );
}
// Helper method to iterate at compile time
private void applyTravers(size_t index, T...)(auto ref T rhs)
if(isIterable!T && !isTuple!T)
{
if(rhs.empty) return;
items.set!index(cast(args_t!index) rhs.front);
rhs.popFront();
static if(index < Args.length) {
applyTravers!(index+1, T)(forward!rhs);
}
}
// overloading assignment operator for iterable types
void opAssign(T)(auto ref T rhs)
if(isIterable!T && !isTuple!T)
{
applyTravers!(0, T)( forward!rhs );
}
}
// to run tests: dmd -unittest -main vlm/utils/destructing.d && ./vlm/utils/destructing
// or: rdmd -unittest -main vlm/utils/destructing.d
nothrow unittest {
// Traversable
string a, b, c, d, e;
tie(a,b,c,d,e) = ["foo1","foo2","foo3","foo4","foo5"];
assert([a,b,c,d,e] == ["foo1","foo2","foo3","foo4","foo5"]);
tie(a,b,c) = ["bar1","bar2"];
assert([a,b,c] == ["bar1", "bar2", "foo3"]);
tie(a,b,c);
assert([a,b,c] == ["bar1", "bar2", "foo3"]);
tie(c,c,c,d,e) = ["hru1","hru2"];
assert([a,b,c,d,e] == ["bar1","bar2","hru2","foo4","foo5"]);
size_t i, j, k;
tie(i,j,k) = [1,2];
assert([i,j,k] == [1, 2, size_t.init]);
tie(i,j,k) = [50,60,70,80,90];
assert([i,j,k] == [50,60,70]);
tie(i,j,k) = [-1,3.14];
assert([i,j,k] == [size_t.max, 3, 70]);
float pi;
int l;
tie(i,pi,l) = [3.14, 3.14, 3.14];
assert(tuple(i,pi,l) == tuple(size_t(3), float(3.14), 3));
tie(i,pi,l) = [size_t.max, size_t.max, size_t.max];
assert( tuple(i,pi,l) == tuple(size_t.max, float(size_t.max), cast(int) size_t.max ) );
// Tuples
int x;
string y;
char z;
size_t u,v,w;
tie(x,y,z) = tuple(26, " hello ", 'a', 777, 3.14);
assert(x == 26);
assert(y == " hello ");
assert(z == 'a');
tie(x,y,z) = tuple();
assert(x == 26);
assert(y == " hello ");
assert(z == 'a');
tie(x, y, z) = tuple(15, " world ");
assert(x == 15);
assert(y == " world ");
assert(z == 'a');
tie(x, y, z);
assert(x == 15);
assert(y == " world ");
assert(z == 'a');
tie(x, y, z) = tuple();
assert(x == 15);
assert(y == " world ");
assert(z == 'a');
tie(x) = tuple(50);
assert(x == 50);
//tie() = tuple(15, " hello "); <-- don't call with emprty args
tie(x,y,z,u,v,w) = tuple(-5, " hi ", 'b', 48, 49, 50, 777, 3.14);
assert(tuple(x,y,z, [u,v,w]) == tuple(-5, " hi ", 'b', [48, 49, 50]));
tie(u,v,w,y) = tuple(15,16,17);
assert([u,v,w] == [15,16,17]);
tie(v,v,v,y,x,z) = tuple(25,26,27);
assert([u,v,w] == [15,27,17]);
tie(v,u) = tuple(u,v);
assert([u,v] == [27,15]);
}
@valmat
Copy link
Author

valmat commented Feb 20, 2018

A more simple implementation of destruction that only supports iterable types, no tuples:
https://gist.github.com/valmat/bdcf24e3365d202aaae7db6ffbf9429d

@valmat
Copy link
Author

valmat commented Feb 20, 2018

@valmat
Copy link
Author

valmat commented Mar 2, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment