0

At

https://github.com/nordlow/phobos-next/blob/03b4736fdd65ef84c6fc583eddee4196629cea81/src/variant_arrays.d

I've implemented a lightweight-polymorphic array container I call VariantArrays(Types...) indexed by a corresponding polymorphic index I call VariantIndex(Types...).

It uses .mangleof together with mixins to automatically infer the definition (including its name) of each array store for each element type in Types. The element types are passed in the template parameter Types to the two templated structs mentioned above.

Everything works except for when I try to instantiate VariantArrays from within a module other than variant_arrays.d. For instance, if I try to use it in another module containing

unittest
{
    struct S { int x; }
    import variant_arrays : VariantArrays;
    VariantArrays!S a;
}

I get the error

variant_arrays.d-mixin-130(130,1): Error: undefined identifier `S`
foo.d(5,5): Error: template instance variant_arrays.VariantArrays!(S) error instantiating

In other words, the symbol S cannot be resolved in the scope of VariantArrays eventhough it's feed as a template parameter.

Is there a way around this problem?

Posted also here: http://forum.dlang.org/post/einvuqvpeoeubkwhwbbm@forum.dlang.org

Here follows the definition of variant_arrays.d (excluding unittests):

/** Polymorphic index into an element in `VariantArrays`. */
private struct VariantIndex(Types...)
{
    import std.meta : staticIndexOf;
private:
    alias Kind = ubyte;              // kind code
    alias Size = size_t;             // size type

    import bit_traits : bitsNeeded;

    /// Number of bits needed to represent kind.
    enum kindBits = bitsNeeded!(Types.length);

    /// Get number kind of kind type `SomeKind`.
    enum nrOfKind(SomeKind) = staticIndexOf!(SomeKind, Types); // TODO cast to ubyte if Types.length is <= 256

    /// Is `true` iff an index to a `SomeKind`-kind can be stored.
    enum canReferTo(SomeKind) = nrOfKind!SomeKind >= 0;

    /// Construct.
    this(Kind kind, Size index) // TODO can ctor inferred by bitfields?
    {
        _kindNr = kind;
        _index = index;
    }

    import std.bitmanip : bitfields;
    mixin(bitfields!(Size, "_index", 8*Size.sizeof - kindBits,
                     Kind, "_kindNr", kindBits));
}

/** Stores set of variants.

    Enables lightweight storage of polymorphic objects.

    Each element is indexed by a corresponding `VariantIndex`.
 */
private struct VariantArrays(Types...)
{
    alias Index = VariantIndex!Types;

    import basic_copyable_array : CopyableArray;

    /// Returns: array type (as a string) of `Type`.
    private static immutable(string) arrayTypeString(Type)()
    {
        return `CopyableArray!(` ~ Type.stringof ~ `)`;
    }

    /// Returns: array instance (as a strinng) storing `Type`.
    private static immutable(string) arrayInstanceString(Type)()
    {
        return `_values` ~ Type.mangleof;
    }

    /** Insert `value` at back.
     */
    pragma(inline)                             // DMD cannot inline
    Index insertBack(SomeKind)(SomeKind value) // TODO add array type overload
        if (Index.canReferTo!SomeKind)
    {
        mixin(`alias arrayInstance = ` ~ arrayInstanceString!SomeKind ~ `;`);
        const currentIndex = arrayInstance.length;
        arrayInstance.insertBack(value);
        return Index(Index.nrOfKind!SomeKind,
                     currentIndex);
    }
    alias put = insertBack;     // polymorphic `OutputRange` support

    /// Get reference to element of type `SomeKind` at `index`.
    scope ref inout(SomeKind) at(SomeKind)(in size_t index) inout return
        if (Index.canReferTo!SomeKind)
    {
        mixin(`return ` ~ arrayInstanceString!SomeKind ~ `[index];`);
    }

    /// Peek at element of type `SomeKind` at `index`.
    scope inout(SomeKind)* peek(SomeKind)(in Index index) inout return @system
        if (Index.canReferTo!SomeKind)
    {
        if (Index.nrOfKind!SomeKind == index._kindNr)
        {
            return &at!SomeKind(index._index);
        }
        else
        {
            return null;
        }
    }

private:
    // TODO this fails:
    mixin({
            string s = "";
            foreach (Type; Types)
            {
                s ~= arrayTypeString!Type ~ ` ` ~ arrayInstanceString!Type ~ `;`;
            }
            return s;
        }());
}
Nordlöw
  • 11,838
  • 10
  • 52
  • 99
  • 1
    I see `stringof` being used. That's almost certainly a mistake... make sure you have read my old answer on the topic and see if there's any way you can get rid of those stringof things https://stackoverflow.com/questions/32615733/struct-composition-with-mixin-and-templates/32621854#32621854 – Adam D. Ruppe Sep 27 '17 at 18:39
  • So what do you suggest I use instead of the `.stringof` in `arrayTypeString`? And is my use of `.mangleof` incorrect too? – Nordlöw Sep 27 '17 at 18:59
  • Would it work to make the symbol fully qualified before converting it to a string? – Nordlöw Sep 27 '17 at 19:08
  • 1
    I would get rid of the function entirely and instead use `Types[idx]` inline. arrayInstanceString I think is ok since it is used differently. – Adam D. Ruppe Sep 27 '17 at 19:09
  • Why use `Types[idx]` when I already have `Type` in the (static) foreach? And what should I do with `Types[idx]`? I still need to convert it to a string right? Or is there another way? Can I use template mixins here? Can I define the struct `VariantArrays` inside a template mixin? – Nordlöw Sep 27 '17 at 19:15
  • 1
    Because `Type` is only valid inside the foreach. You are building a string and returning it to an outside scope. `Types[0]` and `Types[1]` etc are valid outside the body of the foreach too. The ONLY thing you should be converting to string is the index. Otherwise, use the local names. The brand new `static foreach` feature might also be usable here but I haven't done enough with it yet to know best. – Adam D. Ruppe Sep 27 '17 at 19:49

0 Answers0