Label: ♦bug report ♦english

[206] Troubles with Dao

Comment
    I've been using Dao for a short time, but already found a lot of unpleasant bugs and other problems. Eventually, I decided to make some sort of a bug report. In this post I described most significant problems I encountered. I hope at least some of them may be fixed soon. By the way, I use Dao 1.1 with threads on Windows XP.
    1) Qt dynamic form loading
    Probably the most serious problem for me. The dynamic loading itself does work, but widget object returning by QUiLoader::load method can be used as QWidget only. I found no way to convert it to QDialog , for instance. And this aspect concerns child widgets as well. By the way, they cannot be accessed through QObject::children method, as it returns some fake object.
    Actually, I attempted to reimplement virtual method createWidget in class derived from QUiLoader to gain access to created widgets, but QUiLoader::load (and entire DVM) simply crashes when my createWidget returns. It does work, however, with standard createWidget method, but, as I mentioned before, it is impossible to use returned object as non- QWidget instance.
    Finally, I also tried to fix this problem through modifying DaoQt source code, but that idea failed as well. I got some mysterious 'collect2' error during linking with no additional messages (It was on Qt SDK 2010.02).
    2) Default values for routine arguments
    It appears that using lists, tuples etc. as default values for one argument of a routine causes strange errors whith other arguments. Few examples:
routine  proc(x  =  {0,  1},  i  =  0)
{
      io.writeln(i); 
}

proc();  # outputs '{0, 1}' instead of '0'

routine  proc2(x  =  {0,  1},  i  =  1)
{
      io.writeln(1); 
}

proc2();  # outputs '?'

routine  proc3(x  =  {0,  1},  i:  int  =  1)
{
      io.writeln(i);
}

proc3();  # outputs '0' instead of '1'

routine  proc4(x  =  {1,  1},  i:  int  =  1)
{
      io.writeln(i);
}

proc4();  # causes error '1, invalid parameter list'
    The problem, however, can be solved trivially:
const  constList  =  {0,  1};

routine  proc(x  =  constList,  i  =  0)
{
      io.writeln(i);
}

proc();  # all works fine
    null also causes troubles:
routine  proc(x  =  null,  i  =  1)
{
      io.writeln(i);
}

proc();  # outputs ‘0’ instead of ‘1’
    Placing argument with null at the end of parameter list make it work properly.
    3) Thread synchronization
    Usual situation: two threads are created simultaneously (actually, consequently without delay) and thread B, for instance, waits for thread A before executing. It may happen that thread B would suddenly (and silently) crash somewhere in the midst of it’s execution after ‘joining’ with thread A. Apparently, placing std.sleep call between creation of A and B threads eliminates the possibility of such accident.
    In fact, this error emerged periodically when I was working with 8 threads, and probably there might be some additional conditions influencing the situation.
    4) Class operators
    In certain circumstances accessing class members in operator function may fail:
classX
{

      classY
      {
            var  yMember  =  0;
      }

      var  inner:  Y;

      routineX()
      {
            inner  =  Y();
      }
      operator  .member=(value:  int)
      {
            inner.yMember  =  value;
      }
}

classZ:  X
{
      routineZ():  X(){}
}

z  =  Z();
z.member  =  1;  # here error ‘ErrorFieldNotExist: yMember’ is raised
    Fortunately, accessing object inner via routine works normally:
classX
{

      classY
      {
            var  yMember  =  0;
      }

      var  inner:  Y;

      routineX()
      {
            inner  =  Y();
      }

      routine  getInner()
      {
            return  inner;
      }

      operator  .member=(value:  int)
      {
            getInner().yMember  =  value;  # using a mediator
      }
}

classZ:  X
{
      routineZ():  X(){}
}

z  =  Z();
z.member  =  1;  # works fine
    5) Apply
    In functional method apply variable for accessing current index in array is always 0:
arr  =  [1  :  5];
apply(arr)  ->  |x,  i|{io.write(i,  ' ')};  # outputs 5 zeros
    6) Using mtlib
    Even with ' use mtlib ' statement it is impossible to use corresponding routines ( thread , mutex etc.) without pointing library name first:
use  mtlib;

routine  proc()
{
      io.writeln('within a thread');
}

t  =  thread(proc);  # raises ‘thread, invalid parameters for the call’
    7) Anonymous routines
    Declaring and using anonymous routine without parameters or return statement results in error and DVM crash accordingly:
x  =  routine(){io.writeln('x');};  # raises ‘inconsistent typing’
x();                                                          # crashes
    Returning some value or adding an argument for the routine above will make it work.
    8) Division by zero
    Unexpectedly results in DVM crash with no messages shown:
x  =  0;
y  =  1/x;
    9) Fake keywords
    Using extern or namespace in code results in immediate termination or hanging of DVM. Fake keyword a sometimes may cause the same problems:
routine  a(){return0;};
io.writeln(a());                # nothing happens (no output)
    It seems that’s all for now. These errors are the most notable amongst what I've seen. I hope they will be taken into account.

Comments
I have spent some time to fix some of the bugs in Qt dynamic form loading. Now it allows deriving from QUiLoader and re-implement the createWidget() method to create customized loader. I tested the following example with Qt4.6.2 on MacOS 10.5, it is reasonably working:
load  DaoQtCore;
load  DaoQtGui  require  DaoQtCore;

app  =  QApplication('test');

file  =  QFile('test/daoAbout2.ui');
file.open(  QFile::ReadOnly  );

class  MyDialog  :  QDialog
{
    sub  MyDialog(  parent  :  QWidget=0)  :  QDialog(  parent  ){}
}

class  MyUiLoader  :  QUiLoader
{
    sub  createWidget(  className  :  string,  parent  :  QWidget=0,  name  :  string=''){
        if(  className  ==  'MyDialog'){
            return  MyDialog(  parent  );
        }     
        return((QUiLoader)self).createWidget(  className,  parent,  name  );
    }
}

loader  =  MyUiLoader();
win  =  loader.load(  file  );
win.show();
app.exec();
However, I didn't do extensive testing yet, but this fixing should also fix some other bugs that are due to improper wrapping code generation. I haven't try to fix the problem of casting from QWidget to other widget types, this will soon be fixed. The problem with the returned object of QObject::children should also be fixable.

You may find the new bindings for Qt4.6.2 (only QtCore and QtGui at this moment) at: http://code.google.com/p/daoqt/source/browse/#svn/trunk/DaoQt4.6

I will also look into the rest of bugs you reported, the updates will be committed to the svn repository of Dao on Google Code, as soon as fixing become available.
This bug is caused by improper mapping of self-object for class operator method calls, namely the z object was not mapped from class type Z to class type X when .member=() of X is called. Now it is fixed and updated to the svn repository.
arr  =  [1  :  5];
apply(arr)  ->  |x,  i|{io.write(i,  ' ')};  # outputs 5 zeros
Here arr was actually treated as a 2-dimensional matrix, with row dimension equal to 1. And the index variable i referred to the index in row which is always zero, only using a second index variable can access the proper index value.

Now it is fixed such that, for vector, both the first and second index variable (if used) in the apply()->|,| structure will refer to the proper index values. So the above codes will print out proper indexes.
use  mtlib; 
routine  proc(){        io.writeln('within a thread');  }
t  =  thread(proc);  # raises ‘thread, invalid parameters for the call’

Actually this is not a bug, the reason that this will raise an exception is that thread() is a method that requires a self parameter which should be mtlib . So the correct way to call thread() here after the use mtlib is:
t  =  thread(  mtlib,  proc  );

The same holds for other methods that require a self parameter.
The bug with list as default parameter values is caused by improper compiling. And the bug with null as default parameter value is caused by improper passing default parameter values at running time, where the default values after a null default value are not passed.

Now they are fixed and updated into the svn repository on google code.
x  =  routine(){io.writeln('x');};  # raises ‘inconsistent typing’
x();                                                          # crashes

This is the result of a bug in handling the abstract types of routines. Now it is fixed (r20 in svn).
This bug is caused by the fact that the main thread may finish sooner than a child thread, so it may happen that the main thread have done clean up to free some data structures that may still be need by those unfinished children threads. As a result, if there are "un-joined" thread running when the main thread reached the end, it always crashes. Sometimes, the children threads are finished, but the buffered output are not completely printed out before the program end, this might be the situation you thought to be silent crash.

Now this bug is fixed (in revision r24) by letting the main thread to wait for un-joined threads, and print out a warning message saying that there are un-joined threads. Also fixed a potential bug in thread creation.
Now a static method qobject_cast() is added to each type derived from QObject , so that, X.qobject_cast( Y ) or X::qobject_cast( Y ) will cast type Y to type X , just in the same way as qobject_cast<X*>(Y*) in C++.

Moreover, the binding codes are improved such that, for QObject or its derived class, say X and Y , where Y is a parent class of X , if X is created in Dao codes, and passed to C++, and then passed back to Dao as type Y , you still get a X , namely it will retain it precise type information. So in the mentioned example,
load  DaoQtCore;
load  DaoQtGui  require  DaoQtCore;

app  =  QApplication('test');
file  =  QFile('test/daoAbout2.ui');
file.open(  QFile::ReadOnly  );

class  MyDialog  :  QDialog
{
    sub  MyDialog(  parent  :  QWidget=0)  :  QDialog(  parent  ){}
}
class  MyUiLoader  :  QUiLoader
{
    sub  createWidget(  className  :  string,  parent  :  QWidget=0,  name  :  string=''){
        if(  className  ==  'MyDialog'){
            return  MyDialog(  parent  );
        }     
        return((QUiLoader)self).createWidget(  className,  parent,  name  );
    }
}

loader  =  MyUiLoader();
win  =  loader.load(  file  );
win.show();
app.exec();
If MyDialog is the top level widget used in daoAbout2.u , loader.load( file ) will return a type of QDialog instead of QWidget , to convert to MyDialog , one can simple do casting,
dialog  =  (MyDialog)  win;
(This require to use the latest version (r29) of Dao from the svn repository) Here, such casting is permitted, because this dialog widget is created in MyUiLoader:: createWidget() by,
if(  className  ==  'MyDialog'){
      return  MyDialog(  parent  );
}
In this way, a Dao object of MyDialog is created, and this Dao object has a parent object that is the type of QDialog , which is then passed back to Dao by,
win  =  loader.load(  file  );
Since win retains its precise type information, casting it to MyDialog becomes valid.

Change picture:

Choose file:

123 4
56 78910 11
121314151617 18
192021222324 25
26272829

fu: ... I forgot to say something about the plan for the whole new year in my previous reply. Well, besides w ... (Jan.19,01:40)

fu: ... Happy new dragon year (which will start from this sunday)! Actually, it was a busy month (I wish th ... (Jan.18,22:46)

ybabel: What's the plan for the new year ? Hello 'vry budy :- ) happy new year (when is the new year for you Fu ?) I saw you come back and comm ... (Jan.18,18:59)

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