On Wed, Jan 03, 2024 at 04:50:57PM +0000, axricard via Digitalmars-d-learn 
wrote:
> I have an interface that is implemented by many classes, and I want to
> pick one of these implementations at random. There are two more
> constraints : first the distribution is not uniform, all classes can
> define the chance they have to be picked (this is reflected by the
> function 'weight()' below).  And all classes are not always available,
> this depends on some runtime information.

I would tag each implementation with a compile-time enum and use
compile-time introspection with CRTP[1] to auto-generate the code for
choosing a class according to the desired distribution.

[1] https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern


Something like this:


----------SNIP-----------
import std.stdio;

interface MyIntf {
        void work();
}

struct ImplemInfo {
        int weight;
        MyIntf function() instantiate;
}

ImplemInfo[] implems; // list of implementations
int totalWeight;

MyIntf chooseImplem() {
        import std.random;
        auto pick = uniform(0, totalWeight);
        auto slice = implems[];
        assert(slice.length > 0);
        while (slice[0].weight <= pick) {
                pick -= slice[0].weight;
                slice = slice[1 .. $];
        }
        return slice[0].instantiate();
}

// Base class that uses CRTP to auto-register implementations in
// .implems without needing too much boilerplate in every
// subclass.
class Base(C) : MyIntf {
        // Derived class must define a .weight member readable
        // at compile-time.
        static assert(is(typeof(C.weight) : int),
                "Derived class must define .weight");

        static this() {
                implems ~= ImplemInfo(C.weight, () {
                        return cast(MyIntf) new C;
                });
                totalWeight += C.weight;
        }

        // Derived classes must implement this
        abstract void work();
}

// These classes can be anywhere
class Implem1 : Base!Implem1 {
        enum weight = 1;
        override void work() { writeln(typeof(this).stringof); }
}

class Implem2 : Base!Implem2 {
        enum weight = 2;
        override void work() { writeln(typeof(this).stringof); }
}

class Implem3 : Base!Implem3 {
        enum weight = 3;
        override void work() { writeln(typeof(this).stringof); }
}

void main() {
        // pipe output of program to `sort | uniq -c` to verify that the
        // required distribution is generated correctly.
        foreach (_; 0 .. 100) {
                auto impl = chooseImplem();
                impl.work();
        }
}
----------SNIP-----------


--T

Reply via email to