Skip to content

Commit f12c860

Browse files
committed
Merge pull request #896 from denis-sh/std.conv.emplace-fixes
`std.conv.emplace` fixes & improvements
2 parents 0fcca44 + 1450381 commit f12c860

3 files changed

Lines changed: 292 additions & 108 deletions

File tree

std/conv.d

Lines changed: 194 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3412,10 +3412,9 @@ as $(D chunk)).
34123412
T* emplace(T)(T* chunk)
34133413
if (!is(T == class))
34143414
{
3415-
auto result = cast(typeof(return)) chunk;
3416-
static T i;
3417-
memcpy(result, &i, T.sizeof);
3418-
return result;
3415+
static T i; // Can't use `= T.init` here because of @@@BUG8902@@@.
3416+
memcpy(chunk, &i, T.sizeof);
3417+
return chunk;
34193418
}
34203419
///ditto
34213420
T* emplace(T)(T* chunk)
@@ -3425,6 +3424,64 @@ T* emplace(T)(T* chunk)
34253424
return chunk;
34263425
}
34273426

3427+
version(unittest) private struct __conv_EmplaceTest
3428+
{
3429+
int i = 3;
3430+
this(int i)
3431+
{
3432+
assert(this.i == 3 && i == 5);
3433+
this.i = i;
3434+
}
3435+
this(int i, ref int j)
3436+
{
3437+
assert(i == 5 && j == 6);
3438+
this.i = i;
3439+
++j;
3440+
}
3441+
3442+
@disable:
3443+
this();
3444+
this(this);
3445+
void opAssign();
3446+
}
3447+
3448+
version(unittest) private class __conv_EmplaceTestClass
3449+
{
3450+
int i = 3;
3451+
this(int i)
3452+
{
3453+
assert(this.i == 3 && i == 5);
3454+
this.i = i;
3455+
}
3456+
this(int i, ref int j)
3457+
{
3458+
assert(i == 5 && j == 6);
3459+
this.i = i;
3460+
++j;
3461+
}
3462+
}
3463+
3464+
unittest
3465+
{
3466+
struct S { @disable this(); }
3467+
S s = void;
3468+
static assert(!__traits(compiles, emplace(&s)));
3469+
}
3470+
3471+
unittest
3472+
{
3473+
interface I {}
3474+
class K : I {}
3475+
3476+
K k = void;
3477+
emplace(&k);
3478+
assert(k is null);
3479+
3480+
I i = void;
3481+
emplace(&i);
3482+
assert(i is null);
3483+
}
3484+
34283485

34293486
/**
34303487
Given a pointer $(D chunk) to uninitialized memory (but already typed
@@ -3444,39 +3501,144 @@ T* emplace(T, Args...)(T* chunk, Args args)
34443501
return chunk;
34453502
}
34463503

3504+
unittest
3505+
{
3506+
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
3507+
int a;
3508+
int b = 42;
3509+
assert(*emplace!int(&a, b) == 42);
3510+
}
3511+
3512+
unittest
3513+
{
3514+
interface I {}
3515+
class K : I {}
3516+
3517+
K k = null, k2 = new K;
3518+
assert(k !is k2);
3519+
emplace!K(&k, k2);
3520+
assert(k is k2);
3521+
3522+
I i = null;
3523+
assert(i !is k);
3524+
emplace!I(&i, k);
3525+
assert(i is k);
3526+
}
3527+
34473528
// Specialization for struct
3448-
T* emplace(T, Args...)(T* chunk, Args args)
3529+
T* emplace(T, Args...)(T* chunk, auto ref Args args)
34493530
if (is(T == struct))
34503531
{
3451-
auto result = cast(typeof(return)) chunk;
3452-
34533532
void initialize()
34543533
{
3455-
static T i;
3456-
memcpy(chunk, &i, T.sizeof);
3534+
if(auto p = typeid(T).init().ptr)
3535+
memcpy(chunk, p, T.sizeof);
3536+
else
3537+
memset(chunk, 0, T.sizeof);
34573538
}
34583539

3459-
static if (is(typeof(result.__ctor(args))))
3540+
static if (is(typeof(chunk.__ctor(args))))
34603541
{
34613542
// T defines a genuine constructor accepting args
34623543
// Go the classic route: write .init first, then call ctor
34633544
initialize();
3464-
result.__ctor(args);
3545+
chunk.__ctor(args);
34653546
}
34663547
else static if (is(typeof(T(args))))
34673548
{
34683549
// Struct without constructor that has one matching field for
34693550
// each argument
3470-
*result = T(args);
3551+
*chunk = T(args);
34713552
}
34723553
else //static if (Args.length == 1 && is(Args[0] : T))
34733554
{
34743555
static assert(Args.length == 1);
34753556
//static assert(0, T.stringof ~ " " ~ Args.stringof);
34763557
// initialize();
3477-
*result = args[0];
3558+
*chunk = args[0];
34783559
}
3479-
return result;
3560+
return chunk;
3561+
}
3562+
3563+
// Test constructor branch
3564+
3565+
unittest
3566+
{
3567+
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
3568+
struct S
3569+
{
3570+
double x = 5, y = 6;
3571+
this(int a, int b)
3572+
{
3573+
assert(x == 5 && y == 6);
3574+
x = a;
3575+
y = b;
3576+
}
3577+
}
3578+
3579+
auto s1 = new void[S.sizeof];
3580+
auto s2 = S(42, 43);
3581+
assert(*emplace!S(cast(S*) s1.ptr, s2) == s2);
3582+
assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45));
3583+
}
3584+
3585+
unittest
3586+
{
3587+
__conv_EmplaceTest k = void;
3588+
emplace(&k, 5);
3589+
assert(k.i == 5);
3590+
}
3591+
3592+
unittest
3593+
{
3594+
int var = 6;
3595+
__conv_EmplaceTest k = void;
3596+
emplace(&k, 5, var);
3597+
assert(k.i == 5);
3598+
assert(var == 7);
3599+
}
3600+
3601+
// Test matching fields branch
3602+
3603+
unittest
3604+
{
3605+
struct S { uint n; }
3606+
S s;
3607+
emplace!S(&s, 2U);
3608+
assert(s.n == 2);
3609+
}
3610+
3611+
unittest
3612+
{
3613+
struct S { int a, b; this(int){} }
3614+
S s;
3615+
static assert(!__traits(compiles, emplace!S(&s, 2, 3)));
3616+
}
3617+
3618+
unittest
3619+
{
3620+
struct S { int a, b = 7; }
3621+
S s1 = void, s2 = void;
3622+
3623+
emplace!S(&s1, 2);
3624+
assert(s1.a == 2 && s1.b == 7);
3625+
3626+
emplace!S(&s2, 2, 3);
3627+
assert(s2.a == 2 && s2.b == 3);
3628+
}
3629+
3630+
// Test assignment branch
3631+
3632+
// FIXME: no tests
3633+
3634+
private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName)
3635+
{
3636+
enforceEx!ConvException(chunk.length >= typeSize,
3637+
xformat("emplace: Chunk size too small: %s < %s size = %s",
3638+
chunk.length, typeName, typeSize));
3639+
enforceEx!ConvException((cast(size_t) chunk.ptr) % typeAlignment == 0,
3640+
xformat("emplace: Misaligned memory block (0x%X): it must be %s-byte aligned for type %s",
3641+
chunk.ptr, typeAlignment, typeName));
34803642
}
34813643

34823644
/**
@@ -3492,14 +3654,11 @@ $(D T) is $(D @safe).
34923654
34933655
Returns: A pointer to the newly constructed object.
34943656
*/
3495-
T emplace(T, Args...)(void[] chunk, Args args) if (is(T == class))
3657+
T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class))
34963658
{
34973659
enum classSize = __traits(classInstanceSize, T);
3498-
enforce(chunk.length >= classSize,
3499-
new ConvException("emplace: chunk size too small"));
3500-
auto a = cast(size_t) chunk.ptr;
3501-
enforce(a % T.alignof == 0, text(a, " vs. ", T.alignof));
3502-
auto result = cast(typeof(return)) chunk.ptr;
3660+
testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof);
3661+
auto result = cast(T) chunk.ptr;
35033662

35043663
// Initialize the object in its pre-ctor state
35053664
(cast(byte[]) chunk)[0 .. classSize] = typeid(T).init[];
@@ -3520,6 +3679,14 @@ T emplace(T, Args...)(void[] chunk, Args args) if (is(T == class))
35203679
return result;
35213680
}
35223681

3682+
unittest
3683+
{
3684+
int var = 6;
3685+
auto k = emplace!__conv_EmplaceTestClass(new void[__traits(classInstanceSize, __conv_EmplaceTestClass)], 5, var);
3686+
assert(k.i == 5);
3687+
assert(var == 7);
3688+
}
3689+
35233690
/**
35243691
Given a raw memory area $(D chunk), constructs an object of non-$(D
35253692
class) type $(D T) at that address. The constructor is passed the
@@ -3532,15 +3699,11 @@ $(D T) is $(D @safe).
35323699
35333700
Returns: A pointer to the newly constructed object.
35343701
*/
3535-
T* emplace(T, Args...)(void[] chunk, Args args)
3702+
T* emplace(T, Args...)(void[] chunk, auto ref Args args)
35363703
if (!is(T == class))
35373704
{
3538-
enforce(chunk.length >= T.sizeof,
3539-
new ConvException("emplace: chunk size too small"));
3540-
auto a = cast(size_t) chunk.ptr;
3541-
enforce(a % T.alignof == 0, text(a, " vs. ", T.alignof));
3542-
auto result = cast(typeof(return)) chunk.ptr;
3543-
return emplace(result, args);
3705+
testEmplaceChunk(chunk, T.sizeof, T.alignof, T.stringof);
3706+
return emplace(cast(T*) chunk.ptr, args);
35443707
}
35453708

35463709
unittest
@@ -3559,26 +3722,10 @@ unittest
35593722

35603723
unittest
35613724
{
3562-
debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
3563-
int a;
3564-
int b = 42;
3565-
assert(*emplace!int(&a, b) == 42);
3566-
3567-
struct S
3568-
{
3569-
double x = 5, y = 6;
3570-
this(int a, int b)
3571-
{
3572-
assert(x == 5 && y == 6);
3573-
x = a;
3574-
y = b;
3575-
}
3576-
}
3577-
3578-
auto s1 = new void[S.sizeof];
3579-
auto s2 = S(42, 43);
3580-
assert(*emplace!S(cast(S*) s1.ptr, s2) == s2);
3581-
assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45));
3725+
int var = 6;
3726+
auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var);
3727+
assert(k.i == 5);
3728+
assert(var == 7);
35823729
}
35833730

35843731
unittest
@@ -3617,38 +3764,6 @@ unittest
36173764
assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345]));
36183765
}
36193766

3620-
unittest
3621-
{
3622-
struct Foo
3623-
{
3624-
uint num;
3625-
}
3626-
3627-
Foo foo;
3628-
emplace!Foo(&foo, 2U);
3629-
assert(foo.num == 2);
3630-
}
3631-
3632-
unittest
3633-
{
3634-
interface I {}
3635-
class K : I {}
3636-
3637-
K k = void;
3638-
emplace!K(&k);
3639-
assert(k is null);
3640-
K k2 = new K;
3641-
assert(k2 !is null);
3642-
emplace!K(&k, k2);
3643-
assert(k is k2);
3644-
3645-
I i = void;
3646-
emplace!I(&i);
3647-
assert(i is null);
3648-
emplace!I(&i, k);
3649-
assert(i is k);
3650-
}
3651-
36523767
// Undocumented for the time being
36533768
void toTextRange(T, W)(T value, W writer)
36543769
if (isIntegral!T && isOutputRange!(W, char))

std/traits.d

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,6 +3088,43 @@ unittest
30883088
}
30893089

30903090

3091+
private template maxAlignment(U...) if(isTypeTuple!U)
3092+
{
3093+
static if(U.length == 1)
3094+
enum maxAlignment = U[0].alignof;
3095+
else
3096+
enum maxAlignment = max(U[0].alignof, .maxAlignment!(U[1 .. $]));
3097+
}
3098+
3099+
3100+
/**
3101+
Returns class instance alignment.
3102+
3103+
Example:
3104+
---
3105+
class A { byte b; }
3106+
class B { long l; }
3107+
3108+
// As class instance always has a hidden pointer
3109+
static assert(classInstanceAlignment!A == (void*).alignof);
3110+
static assert(classInstanceAlignment!B == long.alignof);
3111+
---
3112+
*/
3113+
template classInstanceAlignment(T) if(is(T == class))
3114+
{
3115+
alias maxAlignment!(void*, typeof(T.tupleof)) classInstanceAlignment;
3116+
}
3117+
3118+
unittest
3119+
{
3120+
class A { byte b; }
3121+
class B { long l; }
3122+
3123+
static assert(classInstanceAlignment!A == (void*).alignof);
3124+
static assert(classInstanceAlignment!B == long.alignof);
3125+
}
3126+
3127+
30913128
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
30923129
// Type Conversion
30933130
//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://

0 commit comments

Comments
 (0)