Home Documentation Download Blog Forum Projects
Label: ♦english ♦news

[485] New feature for running time class creation

Comment
Running time class creation has been recently supported. This is another new feature besides the meta-fields (see Summary of the recent development of Dao ) and function decorator to support writing more dynamic programs.

The basic syntax is the following:
c = class( name, parents, fields, methods ){ body }
All parameters are optional. Acceptable parameter types:
  • name: string ;
  • parents: list<class> or map<string,class> ;
  • fields: tuple<name:string,value:any> , this tuple may take two optional fields storage:enum<const,global,var> and access:enum<private,protected,public> ;
  • methods: tuple<name:string,method:routine> , this tuple can also take an optional access field. A method with the same name as the value in the name parameter will be added as constructor.
The body part of this syntax is more or less the same as the class body in normal class definition, except that @class should be used in the places where the own class name need to be used. And when used in class methods, fields that are not declared in the class body, namely fields inherited from parents or added from fields , have to be accessed through the self variable.

Some simple examples:
class1 = class(){}
class2 = class(){ var value = 0 }
class3 = class(){
  var value = 0
  routine @class( v = 0 ){ value = v } # constructor
}
o1 = class1();
o2 = class2();
o3 = class3( 123 )

It is also possible to use type holder in such class body and class methods:
routine MakeClass( @TYPE, name = '' )
{
  klass = class( name ){
    var table : array<@TYPE> = [1,2,3];
  }
  return klass;
}
itab = MakeClass( int );
ftab = MakeClass( float );
dtab = MakeClass( double );
it = itab();
ft = ftab();
dt = dtab();
io.writeln( it.table );
io.writeln( ft.table );
io.writeln( dt.table );


A more complex example:
class Base
{
  var name = 'Base';
}

routine CreateClass( name : string, value = 0 )
{
  io.writeln( name, value );

  # @class for host class type
  rout = routine( self : @class ){
    io.writeln( 'Extra()', self.index );
  }
  # to be used as initializer/constructor
  init = routine( self : @class, txt : string ){
    io.writeln( 'initialized by init' );
    self.text = txt;
  }

  # klass = class( name, parents, fields, methods ){ core_class_body }
  klass=class( name,
      { 'Alias'=>Base },
      { ('added', 789, $const, $public) },
      { (name, init), ('Extra', rout) } )
  {
    var index = value;
    var text = '---';

    routine @class( id = 0 ){ index = id }

    routine Meth( id = 0 ){
      io.writeln( 'Meth(id)', id, index, text, self.Alias )
    }
    routine Meth( text : string ){
      io.writeln( 'Meth(text)', self.name, self.added )
    }
  }
  return klass;
}

klass1 = CreateClass( 'AA', 123 );
klass2 = CreateClass( 'BB', 456 );

obj1 = klass1( 'abcdef' );
obj2 = klass2( 123456 );

obj1.Meth();
obj2.Meth();
obj1.Meth( 'abc' );
obj2.Meth( 'def' );
obj1.Extra();

obj1.added = 987;
io.writeln( obj1.added ); # error, accessing private member


PS: based on the implementation of this feature, C++ like template will also be supported in the near future!
Comments
Now a preliminary implementation of template class support has just been finished and committed into the code repository (revision 124: a44524c5c240). An example of template class is add in the demo fold. Here is that example:
class  Item<@V>
{
        varvalue  :  @V; 
        var  next  :  Item;

        routine  Item(  v  :  @V  ){value  =  v  }
}

class  LinkList<@V,@T=int>
{
        var  first  :  Item<@V>;
        var  last    :  Item<@V>;

        routine  Append(value  :  @V  ){
                item  =  Item<@V>(value);
                if(  first  ==  null  )  first  =  item  else  last.next  =  item;
                last  =  item;
        }
        routine  Print(){
                item  =  first;
                io.writeln(  item.value,  item  );
                while(  item.next  !=  null  ){
                        item  =  item.next;
                        io.writeln(  item.value,  item  );
                }
        }
}

ll  =  LinkList<int>();
ll.Append(11);
ll.Append(22);
ll.Append(33);
ll.Print();

I wonder if we should now organize the standard template library -- I already have some ideas... :)
Do you mean the built-in container types such as "list/map/..."? I am not sure about this. They behavior more like just type names, they do not instantiate in the same way as template classes do. Actually, they only instantiate their typing interfaces. Moreover, there are types such as "routine<...>" etc. which assume template like form, but are really just type names (typing interfaces). Anyway, I'd like to hear you ideas:)
No, my thought is to create a standard Dao library for useful template classes (no changes to the DaoVM itself). For instance, I've thought up few templates providing a high-level interface to mtlib : no explicit threads/mutexes, just function/variable wrappers (the idea was partially inspired by QtConcurrent ). While direct mtlib capabilities are required to organize flexible multithreading, high-level wrappers may be used for simpler, typical tasks (e.g., calculate the function asynchronously).
That is, the standard library could be used to supply the language -- to provide specialized wrappers for various tasks. Of course, most templates would be relatively simple, but it is still better to have a ready-to-use concept than to devise and implement the same thing by oneself.
Cannot say I'm confident of the benefits of having such library, but it seems like an interesting idea to me. Let me show what templates I've thought up so far (but have not tested yet).
First, three templates useful for multithreading programming. SharedVariable provides the safe way of using a global variable by several threads: a thread should "seize" the variable, use it, and then "release" to make it accessible from other threads (which will have to wait until that happens -- "unauthorized" access is prohibited). To put it simply, it's a synchronized inter-thread global variable:
class  SharedVariable<@T>
{
private

     var  data:  @T;
     var  mtx:  mutex  =  mtlib.mutex();
     var  owner:  thread  =  null;

public

     routine  MTVariable(value:  @T)
     {
          data  =  value;
     }

     routinevalue()
     {
          if(owner  !=  null  and  owner  !=  mtlib.self())
               std.error('The variable is locked');

          return  data;
     }

     routine  setvalue(value:  @T)
     {
          if(owner  !=  null  and  owner  !=  mtlib.self())
               std.error('The variable is locked');

          data  =  value;
     }

     routine  seize()
     {
          mtx.lock();
          owner  =  mtlib.self();
     }

     routine  release()
     {
          if(owner  ==  mtlib.self())
               mtx.unlock();
     }

     routine  trylock()
     {
          if(mtx.trylock())
          {
               owner  =  mtlib.self();
               return1;
          }
          else
               return0;
     }
}
The next template, JoinVariable , is designed to be used as a thread function result. It can be accessed freely by any thread, but will implicitly join the function's thread if necessary. It's a direct analog of QFuture class -- the synchronized result of asynchronous function:
class  JoinVariable<@T>
{
private

     data:  @T;
     owner:  thread  =  null;

public

     routine  JoinVariable(value:  @T)
     {
          data  =  value;
     }

     routine  seize()
     {
          owner  =  mtlib.self();
     }

     routinevalue()
     {
          if(owner  !=  null  and  owner  !=  mtlib.self())
               owner.join();

          return  data;
     }

     routine  setvalue(value:  @T)
     {
          if(owner  !=  null  and  owner  !=  mtlib.self())
               owner.join();

          data  =  value;
     }

     routine  release()
     {
          if(owner  ==  mtlib.self())
               owner  =  null;
     }

     routine  isowned()
     {
          return  owner  !=  null;
     }
}
Finally, ThreadFunction template encapsulating asynchronous function; its result is provided as a JoinVariable . Multiple parameters may be passed via tuple or list:
class  ThreadFunction<@Arg,  @Res>
{
private

     var  func:  routine<@Arg=>@Res>;
     var  res:  JoinVariable<@Res>();
     var  fthread:  thread;
     var  isrunning  =  0;

     routine  run(param:  @Arg)
     {
          res.sieze();
          res.setvalue(proc(param));
          res.release();
          isrunning  =  0;
     }

public

     routine  ThreadFunction(f:  routine<@Arg=>@Res>,  result:  @Res)
     {
          func  =  r;
          res  =  JoinVariable<@Res>(result);
     }

     operator()(param:  @Arg)
     {
          if(isrunning)
               std.error('The thread is still running');

          fthread  =  mtlib.thread(run{param});
          isrunning  =  1;
          return  res;
     }

     routine  result()
     {
          return  res;
     }
}

#AN EXAMPLE

routine  f(x:  int)
{
     #do something useful
}

func  =  ThreadFunction<@int>(f,  0);
res  =  func(10);
...
x  =  res.value();  #joins implicitly if needed
But enough with multithreading. I also created CachedFunction template which represents a "pure" function whose result is cached in hash-map:
class  CachedFunction<@Arg,  @Res>
{
private

     func:  routine<@Arg=>@Res>;
     hash:  map<@Arg,  @Res>  =  {:};

public

     routine  CachedFunction(f:  routine<@Arg=>@Res>)
     {
          func  =  f;
     }

     operator()(param:  @Arg)
     {
          (found,  key,  value)  =  hash.find(param);

          if(found)
               returnvalue;
          else
          {
               value  =  func(param);
               hash[param]  =  value;
               returnvalue;
          }
     }
}
As you can see, such library may as well serve as a proving ground for various insane ideas :)
OK, you are actually proposing create a new standard template library, I have thought you were talking about something already exists in Dao. I think you have a good point on the fact that it is better to have something ready-to-use for typical tasks. And as a proving ground, some nice (or insane in a nice way) idea could actually come out of it. Your examples are interesting.
Wow, this is a really hot new feature! Congratulations, I like it a lot!
Let me share the way of thoughts I had right now:

It would be cool now to be able to add methods and attributes to a class at run-time. It fits well with this addition and makes the language more dynamic than most "modern" languages. Then I thought about the meta-fields, they do exactly this! It's just that they have a different syntax.
bla.bli()# Calling a "usual" method of an object.
bla->bli2()# Calling an "added" method by meta-field of an object.

So what I am wondering right now is if it is a good idea (or a bad idea) to unify the syntax, such that syntactically and semantically to the user, meta fields "feel" just like usual attributes/methods. What do you think?
In reply to TheTrueNightWalker, I like your idea of a high-level standard library. Your examples show a very good use-case of it.
What I wonder is if whe should distribute (and manage) it with dao right away, or as a separate project.
Also, we'd better define some guidelines (not hard rules) right at the beginning to help us decide what fits in and what not - just to keep it at a consistent abstraction level.
Not sure run-time field addition to a class is a good idea, as allowing this would make the concept of classes meaningless -- it would be just a primitive prototype-based OOP. Currently classes are used to specify the usage interface for objects. If new fields are added to a class after it has been stated, it becomes something else -- and the whole Dao typing system becomes useless here! I think meta-fields facility is a nicer and clearer solution to what you want.
Thanks for supporting my idea with the template library; I had doubts about it's reasonableness, but now I think it would really be convenient to have a place to 'store' various design patterns.
Indeed, running time field addition or normal/meta field unification will render the typing system useless. It will also cause dilemma situations such as obj.field=1 , when field does not exist, what to do, raise an exception or add a new field automatically? Current distinction between . and -> will make it clear what's the proper interpretation for such statement, and make prototype-based OOP codes easily recognizable.
Right, sounds scary but I always forget that Dao is strictly typed :-P Because it has a mix of features from both typed and untyped languages. Thank you for the clarifications.

Change picture:

Choose file:

1234 5
67891011 12
131415161718 19
202122232425 26
2728293031

fu: A little bit game development in Dao! Thanks to ClangDao, it has become very easy to create bindings for C/ C++ libraries. The latest one i ... (May.14,07:08)

dao: Dao 1.2 Beta1 is released! After a very long time of development, the first beta release for Dao 1.2 is finally available ( http ... (May.06,23:37)

fu: ... Just to mention: a couple of demos (including the 2000 line one) has been successfully ported to IPho ... (May.19,02:43)

fu: ... Yes, it is getting mature, and more libraries and modules are coming out, hehe:) For GameKit, unfor ... (May.19,02:38)

Pompei2: ... This is cool news and really shows that ClangDao is getting mature, thumbs up. Too bad for this litt ... (May.18,09:17)

fu: ... Not completely, but mostly. New revisions will be regularly pushed to the repository on google code ( ... (May.08,22:38)

Pompei2: ... If I understand it correctly, you want to completely switch? If so then: (May.08,08:46)

This site is powered by Dao
Copyright (C) 2009-2012, daovm.net.
Webmaster: admin@daovm.net