www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Syntactic Sugar for Virtual Constructors?

reply "Janice Caron" <caron800 googlemail.com> writes:
Something Andrei said got me thinking.

I have a need for so-called "virtual constructors", and of course D
has Object.factory() which just does the job, given the classname.
(/Much/ nicer than C++!). But, in my case, the discriminator string
won't be the class name (which may be unknown to the user), but one of
a number of IANA-registered strings, documented in some internet RFC
somewhere. So to deal with that, I need a "factory". The usual way of
doing this is, given

    string s = whatever; // official name

something like:

    auto x = MyClassFactory.create(s);

And that's OK, but it does mean that polymorphic creation is still
"different" from normal creation. I got thinking, wouldn't it be nice
if I could just do

    auto x = new MyClass(s);

and have it work. Now bear in mind that MyClass is an /abstract/
class. It can never be constructed. Only subclasses can be
constructed, and that, of course, is the job of a factory. What I'm
suggesting, then, is just syntactic sugar. It won't give us anything
we can't do already, but it will let us do it with a nicer syntax.

To make it work, you'd have to allow a class to have a factory within
itself, and so what I propose is the following syntax:

    abstract class MyClass
    {
        static string opFactory(/*params*/)
        {
            /* returns a string */
        }
    }

Notice that I typed the opFactory function to return a string. The idea is that

    auto x = new MyClass(s);

would be rewritten by the compiler as

    auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s)));

whenever the class is abstract and static opFactoryis present. So what
opNew needs to return is the string to pass to Object.factory. Observe
the explicit cast in the rewritten line. That is there partly so that
x gets the right type (at compile time), but also to ensure at runtime
that the factory-created class is in fact a subclass of MyClass!

(enforce is one of Andrei's new templates that throws an exception if
the value is non-zero. I think it will also check for null, but if
not, I'm sure it can be made to).

I'm sure that better alternative names for opFactory can be thought
up. Off the top of my head, I came up with opNew, opClass, opNewName,
opClassName and opFactoryName.

Thoughts?
Feb 28 2008
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 Something Andrei said got me thinking.

 I have a need for so-called "virtual constructors", and of course D
 has Object.factory() which just does the job, given the classname.
 (/Much/ nicer than C++!). But, in my case, the discriminator string
 won't be the class name (which may be unknown to the user), but one of
 a number of IANA-registered strings, documented in some internet RFC
 somewhere. So to deal with that, I need a "factory". The usual way of
 doing this is, given

    string s = whatever; // official name

 something like:

    auto x = MyClassFactory.create(s);

 And that's OK, but it does mean that polymorphic creation is still
 "different" from normal creation. I got thinking, wouldn't it be nice
 if I could just do

    auto x = new MyClass(s);

 and have it work. Now bear in mind that MyClass is an /abstract/
 class. It can never be constructed. Only subclasses can be
 constructed, and that, of course, is the job of a factory. What I'm
 suggesting, then, is just syntactic sugar. It won't give us anything
 we can't do already, but it will let us do it with a nicer syntax.

 To make it work, you'd have to allow a class to have a factory within
 itself, and so what I propose is the following syntax:

    abstract class MyClass
    {
        static string opFactory(/*params*/)
        {
            /* returns a string */
        }
    }

 Notice that I typed the opFactory function to return a string. The idea is 
 that

    auto x = new MyClass(s);

 would be rewritten by the compiler as

    auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s)));

 whenever the class is abstract and static opFactoryis present. So what
 opNew needs to return is the string to pass to Object.factory. Observe
 the explicit cast in the rewritten line. That is there partly so that
 x gets the right type (at compile time), but also to ensure at runtime
 that the factory-created class is in fact a subclass of MyClass!

 (enforce is one of Andrei's new templates that throws an exception if
 the value is non-zero. I think it will also check for null, but if
 not, I'm sure it can be made to).

 I'm sure that better alternative names for opFactory can be thought
 up. Off the top of my head, I came up with opNew, opClass, opNewName,
 opClassName and opFactoryName.

 Thoughts?
what about static opCall? class MyClass { static MyClass opCall(string s) { // have to implement factoryString return cast(MyClass)Object.factory(factoryString(s)); } } auto x = MyClass(s); then you could also make the opCall method a mixin to avoid reimplementation. -Steve
Feb 28 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 28/02/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 what about static opCall?
Ooh - I never thought of that. That's close. In the past, what I've done is to build a standalone function (not a member function) called new_MyClass(), which is only an underscore different from the standard "new" function. Ditching the word "new" altogether is attractive. But to clarify - it's the syntactic sugar of being able to say new AbstractBaseClass(...) (and end up with a derived class) that I was asking for. I am well aware that we already have the functionality, but the point of the request /is/ the sugar. It's not that I want the syntax to be "simpler than it is now", or something, it's that I want it to be /identical/ other uses of new. It's just one of those nice little touches that would make D seem more cool. :-)
Feb 28 2008
parent reply BCS <BCS pathlink.com> writes:
Janice Caron wrote:
 On 28/02/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 
what about static opCall?
Ooh - I never thought of that. That's close. In the past, what I've done is to build a standalone function (not a member function) called new_MyClass(), which is only an underscore different from the standard "new" function. Ditching the word "new" altogether is attractive. But to clarify - it's the syntactic sugar of being able to say new AbstractBaseClass(...)
how about allow this functions in abstract classes abstract class AbstractBaseClass { AbstractBaseClass this(string s, int i) { switch(s) { case "Foo": return new DerivedClass(i); ... default: return null; // check code causes exception } } } the return type could be used to detect the usage.
 (and end up with a derived class) that I was asking for. I am well
 aware that we already have the functionality, but the point of the
 request /is/ the sugar. It's not that I want the syntax to be "simpler
 than it is now", or something, it's that I want it to be /identical/
 other uses of new. It's just one of those nice little touches that
 would make D seem more cool. :-)
Feb 28 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 28/02/2008, BCS <BCS pathlink.com> wrote:
         AbstractBaseClass this(string s, int i)
         {
                 switch(s)
                 {
                         case "Foo": return new DerivedClass(i);
                         ...
                         default: return null;
                                 // check code causes exception
                 }
         }
The switch is the one thing that you /must not have/. The factory cannot possibly know all possible (present and future) derived classes. Putting that switch statement there prevents anyone without access to the source code from making new derived classes, making the mechanism completely unworkable for a library. Also, this mechanism needs to import all of the module(s) where the derived class(es) is/are defined, which is also bad. Also, the function above would cause "executable bloat", whereby every possible derived class will end up in every executable which calls that function, whether needed or not. The good way to implement a factory function is to call the function object.Factory(). That way, the factory function needs /no knowledge/ of the derived class, beyond it's class name, extendability is retained, and executable bloat is avoided.
Feb 28 2008
next sibling parent BCS <BCS pathlink.com> writes:
Janice Caron wrote:
 On 28/02/2008, BCS <BCS pathlink.com> wrote:
 
        AbstractBaseClass this(string s, int i)
        {
                switch(s)
                {
                        case "Foo": return new DerivedClass(i);
                        ...
                        default: return null;
                }
        }
your point is valid (in some cases) but totally orthogonal to mine. Re wright it as this: AbstractBaseClass this(string s, int i) { // code to return a sub type of // AbstractBaseClass based on value of s // or null on an argument error so that // check code causes exception } and no important feature of my suggestion is changed but your point vanishes.
 
 The switch is the one thing that you /must not have/. The factory
 cannot possibly know all possible (present and future) derived
 classes.
But it might /in some cases/ known all the /valid/ ones
 Putting that switch statement there prevents anyone without
 access to the source code from making new derived classes,
Re-read that last, but in a "good thing" context. in some cases it will be a good thing.
 making the
 mechanism completely unworkable for a library.
over generalized: unworkable for /some/ (or even many) libraries
 Also, this mechanism
 needs to import all of the module(s) where the derived class(es)
 is/are defined, which is also bad.
In line with the above point of view; this is unavoidable for some cases.
 Also, the function above would
 cause "executable bloat", whereby every possible derived class will
 end up in every executable which calls that function, whether needed
 or not.
Every possible derived class that can get used at run time will have to be available some how for any solution. The only escape would be lazy loading of DLL's or SO's and that can be used in my solution as well.
 
 The good way to implement a factory function is to call the function
 object.Factory(). That way, the factory function needs 
 [the function will have] /no knowledge/
 of the derived class, beyond it's class name,
read that in a "bad thing" context. Again, it can be. BTW, I was not proposing that my solution is a better way to do what you are looking to do, I'm proposing it as a clean solution that can do something more general than what you are looking for.
Feb 28 2008
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 28/02/2008, BCS <BCS pathlink.com> wrote:
         AbstractBaseClass this(string s, int i)
         {
                 switch(s)
                 {
                         case "Foo": return new DerivedClass(i);
                         ...
                         default: return null;
                                 // check code causes exception
                 }
         }
The switch is the one thing that you /must not have/. The factory cannot possibly know all possible (present and future) derived classes. Putting that switch statement there prevents anyone without access to the source code from making new derived classes, making the mechanism completely unworkable for a library. Also, this mechanism needs to import all of the module(s) where the derived class(es) is/are defined, which is also bad. Also, the function above would cause "executable bloat", whereby every possible derived class will end up in every executable which calls that function, whether needed or not. The good way to implement a factory function is to call the function object.Factory(). That way, the factory function needs /no knowledge/ of the derived class, beyond it's class name, extendability is retained, and executable bloat is avoided.
A better way: abstract class AbstractBaseClass { // that type is probably wrong; I can't comprehend how to name // delegate types static AbstractBaseClass delegate ()[string] builders; static AbstractBaseClass factory(string str) { return builders[str](); } } class Derived : AbstractBaseClass { static this () { AbstractBaseClass.builders["something"] = { return new Derived(); } } }
Feb 28 2008
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 Now bear in mind that MyClass is an /abstract/
 class. It can never be constructed. Only subclasses can be
 constructed, and that, of course, is the job of a factory. What I'm
 suggesting, then, is just syntactic sugar. It won't give us anything
 we can't do already, but it will let us do it with a nicer syntax.
But in a fragile way, and bound to the language. A library solution that takes the pain out of writing factories would be more general, more flexible, and less work for Walter. And, hey, it probably does 80% of what you need, today. Except for you having to map IANA strings to classes. And since major dependency injection libraries don't support this, you're SOL: it's not enough of a use case to include there, so it certainly isn't enough of a use case for a language solution.
Feb 28 2008
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Janice Caron wrote:
 Something Andrei said got me thinking.
 
 I have a need for so-called "virtual constructors", and of course D
 has Object.factory() which just does the job, given the classname.
 (/Much/ nicer than C++!). But, in my case, the discriminator string
 won't be the class name (which may be unknown to the user), but one of
 a number of IANA-registered strings, documented in some internet RFC
 somewhere. So to deal with that, I need a "factory". The usual way of
 doing this is, given
 
     string s = whatever; // official name
 
 something like:
 
     auto x = MyClassFactory.create(s);
 
 And that's OK, but it does mean that polymorphic creation is still
 "different" from normal creation. I got thinking, wouldn't it be nice
 if I could just do
 
     auto x = new MyClass(s);
 
 and have it work. Now bear in mind that MyClass is an /abstract/
 class. It can never be constructed. Only subclasses can be
 constructed, and that, of course, is the job of a factory. What I'm
 suggesting, then, is just syntactic sugar. It won't give us anything
 we can't do already, but it will let us do it with a nicer syntax.
 
 To make it work, you'd have to allow a class to have a factory within
 itself, and so what I propose is the following syntax:
 
     abstract class MyClass
     {
         static string opFactory(/*params*/)
         {
             /* returns a string */
         }
     }
 
 Notice that I typed the opFactory function to return a string. The idea is that
 
     auto x = new MyClass(s);
 
 would be rewritten by the compiler as
 
     auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s)));
 
 whenever the class is abstract and static opFactoryis present. So what
 opNew needs to return is the string to pass to Object.factory. Observe
 the explicit cast in the rewritten line. That is there partly so that
 x gets the right type (at compile time), but also to ensure at runtime
 that the factory-created class is in fact a subclass of MyClass!
 
 (enforce is one of Andrei's new templates that throws an exception if
 the value is non-zero. I think it will also check for null, but if
 not, I'm sure it can be made to).
 
 I'm sure that better alternative names for opFactory can be thought
 up. Off the top of my head, I came up with opNew, opClass, opNewName,
 opClassName and opFactoryName.
 
 Thoughts?
Why not just return the instance of MyClass? More flexible, since it doesn't need to have a default constructor in the general case: abstract class MyClass { static string opFactory(/*params*/) { /* returns a MyClass */ } } That would cover the more common case of all the factory's types being known by the implmentation. Then for the case you needed, you can explicitly call Object#factory(string) from within the static opFactory and explicitly do the cast. Still just as nice on the call site, but more flexible for the implementor.
Feb 28 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 28/02/2008, Robert Fraser <fraserofthenight gmail.com> wrote:
 Why not just return the instance of MyClass? More flexible, since it
  doesn't need to have a default constructor in the general case:
No, that would be /less/ flexible, since the factory function would have to know about every possible derived class at compile time. The whole point of a generic factory function is that it has no compile-time knowledge of the derived classes. /All/ information about derived classes is delayed until runtime. The only practical way to do that is to call Object.factory(), and since Object.factory() takes a string parameter, a string is what the function needs to supply.
Feb 28 2008
next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Janice Caron wrote:
 On 28/02/2008, Robert Fraser <fraserofthenight gmail.com> wrote:
 Why not just return the instance of MyClass? More flexible, since it
  doesn't need to have a default constructor in the general case:
No, that would be /less/ flexible, since the factory function would have to know about every possible derived class at compile time. The whole point of a generic factory function is that it has no compile-time knowledge of the derived classes. /All/ information about derived classes is delayed until runtime. The only practical way to do that is to call Object.factory(), and since Object.factory() takes a string parameter, a string is what the function needs to supply.
Why not generate the string and then call Object.factory() from within that virtual constructor if you need the functionality, and for a typical factory that knows about all the subclasses, allow the cool syntax?
Feb 28 2008
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 28/02/2008, Robert Fraser <fraserofthenight gmail.com> wrote:
 Why not just return the instance of MyClass? More flexible, since it
  doesn't need to have a default constructor in the general case:
No, that would be /less/ flexible, since the factory function would have to know about every possible derived class at compile time. The whole point of a generic factory function is that it has no compile-time knowledge of the derived classes. /All/ information about derived classes is delayed until runtime. The only practical way to do that is to call Object.factory(), and since Object.factory() takes a string parameter, a string is what the function needs to supply.
So, generate these strings at runtime? The solution: AbstractBaseClass delegate () [string] builders; class Derived : AbstractBaseClass { static this () { builders["whatever"] = { return new Derived1(); }; } } Of course, you're just talking about syntactic sugar. In this case, I'm quite opposed, since it violates the typical expectation that typeof (new T) == T.
Feb 28 2008
parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 29/02/2008, Christopher Wright <dhasenan gmail.com> wrote:

  class Derived : AbstractBaseClass
  {
     static this ()
     {
        builders["whatever"] = { return new Derived1(); };
     }
  }
Again, that would require the factory function to have compile-time knowledge of derived classes, which is precisely the thing to avoid.
Feb 28 2008
next sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Janice Caron" <caron800 googlemail.com> wrote in message 
news:mailman.89.1204269640.2351.digitalmars-d puremagic.com...
 On 29/02/2008, Christopher Wright <dhasenan gmail.com> wrote:

  class Derived : AbstractBaseClass
  {
     static this ()
     {
        builders["whatever"] = { return new Derived1(); };
     }
  }
Again, that would require the factory function to have compile-time knowledge of derived classes, which is precisely the thing to avoid.
Not necessarily. If Derived were in a shared object, it would be loaded and then its static this() run, which would add itself to the list of builders in the base class, entirely at runtime.
Feb 29 2008
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 29/02/2008, Christopher Wright <dhasenan gmail.com> wrote:
 
  class Derived : AbstractBaseClass
  {
     static this ()
     {
        builders["whatever"] = { return new Derived1(); };
     }
  }
Again, that would require the factory function to have compile-time knowledge of derived classes, which is precisely the thing to avoid.
You could try reading the code you're commenting on :P though there was a typo, I admit. It was the derived class adding a delegate to a static array in the base class. The base class doesn't, technically speaking, know anything about any derived classes, and you don't get any of that circular dependency madness.
Feb 29 2008
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 Something Andrei said got me thinking.
 
 I have a need for so-called "virtual constructors", and of course D
 has Object.factory() which just does the job, given the classname.
 (/Much/ nicer than C++!). But, in my case, the discriminator string
 won't be the class name (which may be unknown to the user), but one of
 a number of IANA-registered strings, documented in some internet RFC
 somewhere. So to deal with that, I need a "factory". The usual way of
 doing this is, given
 
     string s = whatever; // official name
 
 something like:
 
     auto x = MyClassFactory.create(s);
 
 And that's OK, but it does mean that polymorphic creation is still
 "different" from normal creation. I got thinking, wouldn't it be nice
 if I could just do
 
     auto x = new MyClass(s);
 
 and have it work. Now bear in mind that MyClass is an /abstract/
 class. It can never be constructed. Only subclasses can be
 constructed, and that, of course, is the job of a factory. What I'm
 suggesting, then, is just syntactic sugar. It won't give us anything
 we can't do already, but it will let us do it with a nicer syntax.
 
 To make it work, you'd have to allow a class to have a factory within
 itself, and so what I propose is the following syntax:
 
     abstract class MyClass
     {
         static string opFactory(/*params*/)
         {
             /* returns a string */
         }
     }
 
 Notice that I typed the opFactory function to return a string. The idea is that
 
     auto x = new MyClass(s);
 
 would be rewritten by the compiler as
 
     auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s)));
 
 whenever the class is abstract and static opFactoryis present. So what
 opNew needs to return is the string to pass to Object.factory. Observe
 the explicit cast in the rewritten line. That is there partly so that
 x gets the right type (at compile time), but also to ensure at runtime
 that the factory-created class is in fact a subclass of MyClass!
 
 (enforce is one of Andrei's new templates that throws an exception if
 the value is non-zero. I think it will also check for null, but if
 not, I'm sure it can be made to).
 
 I'm sure that better alternative names for opFactory can be thought
 up. Off the top of my head, I came up with opNew, opClass, opNewName,
 opClassName and opFactoryName.
 
 Thoughts?
I don't see why auto x = new MyClass(s); is any better than auto x = MyClass.create(s); Except the first looks like normal construction, even though it's not, while the second screams out clearly "I'm a static factory method". So the second one seems preferable to me. Less magic going on to keep track of in your head. --bb
Feb 29 2008