On 2013-07-16 00:27, H. S. Teoh wrote:

In the spirit of this approach, I've written some C++ code in the past
that looked something like this:

        class BaseClass {
        public:
                // Encapsulate ctor arguments
                struct Args {
                        int baseparm1, baseparm2;
                };
                BaseClass(Args args) {
                        // initialize object based on fields in
                        // BaseClass::Args.
                }
        };

        class MyClass : public BaseClass {
        public:
                // Encapsulate ctor arguments
                struct Args : BaseClass::Args {
                        int parm1, parm2;
                };

                MyClass(Args args) : BaseClass(args) {
                        // initialize object based on fields in args
                }
        };

Basically, the Args structs let the user set up whatever values they
want to, in whatever order they wish, then they are "blessed" into real
class instances by the ctor. Encapsulating ctor arguments in these
structs alleviates the problem of proliferating ctor arguments as the
class hierarchy grows: each derived class simply hands off the Args
struct (which is itself in a hierarchy that parallels that of the
classes) to the base class ctor. All ctors in the class hierarchy needs
only a single (polymorphic) argument.

That's actually quite cleaver.

In D, this approach isn't quite as nice, because D structs don't have
inheritance, so you can't simply pass Args from derived class to base
class. You'd have to explicitly do something like:

        class BaseClass {
        public:
                struct Args { ...  }
                this(Args args) { ... }
        }

        class MyClass {
        public:
                struct Args {
                        BaseClass.Args base;    // <-- explicit inclusion of 
BaseClass.Args
                        ...
                }
                this(Args args) {
                        super(args.base);       // <-- more verbose than just 
super(args);
                        ...
                }
        }

Initializing the args also isn't as nice, since user code will have to
know exactly which fields are in .base and which aren't. You can't just
write, like in C++:

        // C++
        MyClass::Args args;
        args.basefield1 = 123;
        args.field2 = 321;

you'd have to write, in D:

        // D
        MyClass.Args args;
        args.base.basefield1 = 123;
        args.field2 = 321;

which isn't as nice in terms of encapsulation, since ideally user code
should need to care about the exact boundaries between base class and
derived class.

I haven't really thought about how this might be made nicer in D,
though.

On the other hand D supports the following syntax:

MyClass.Args args = { field1: 1, field2: 2 };

Unfortunately that syntax doesn't work for function calls.

--
/Jacob Carlborg

Reply via email to