User:Barre/Slicer: Difference between revisions

From KitwarePublic
Jump to navigationJump to search
m (cla)
mNo edit summary
 
Line 2: Line 2:


* I updated the CMakeLists.txt to match the changes in KWWidgets
* I updated the CMakeLists.txt to match the changes in KWWidgets
* Slicer3 would not compile for Windows in shared mode, because the dll export symbol was missing in all headers in Base/GUI classes; there is a VTK_SLICER_BASE_LOGIC_EXPORT inside Base/Logic, and a VTK_SLICER_BASE_EXPORT in the parent dir. I introduced a new one in the Base/GUI, VTK_SLICER_BASE_GUI_EXPORT, but we have to make sure we want a unique one for each sub-libs in Base/ (this is the case in VTK for each packages for example, say VTK_HYBRID_EXPORT for the Hybrid/ subdir), or a global one (which was the case some time ago in VTK, with a unique VTK_EXPORT for the whole tree). Also, you do not need to prefix the export symbol with VTK_ anymore, SLICER_BASE_EXPORT is fine, I updated the wrappers some time ago to support it :)
* Slicer3 would not compile for Windows in shared mode, for the dll export symbol was missing in the headers in <tt>Base/GUI</tt> classes. I noticed there was a <tt>VTK_SLICER_BASE_LOGIC_EXPORT</tt> inside <tt>Base/Logic</tt>, and a <tt>VTK_SLICER_BASE_EXPORT</tt> in the parent dir, so I introduced a new one in <tt>Base/GUI</tt> (<tt>VTK_SLICER_BASE_GUI_EXPORT</tt>). You may want to make sure you need a unique one for each sub-libs in <tt>Base/</tt> (this is the case in VTK for each of its packages for example, say <tt>VTK_HYBRID_EXPORT</tt> for the Hybrid/ subdir), or just a global one (which was the case some time ago in VTK, with a unique <tt>VTK_EXPORT</tt> for the whole tree). Also, you do not need to prefix the export symbol with VTK_ anymore, <tt>SLICER_BASE_EXPORT</tt> is just fine, I updated the wrappers some time ago to support it :) It's OK to disrespect VTK that way :)
* A set of useful setup paths scripts are now automatically created using KWWidgets macros: check your bin directory for Slicer3SetupPaths.bat (or .sh, .csh), source them so that you can run Slicer3 executable on Win32, especially when it is built in shared lib mode. Without this script, it becomes pretty nightmarish to setup both the PATH, TCLLIBPATH, LD_LIBRARY_PATH (which name changes depending on the OS btw) alltogether. This is especially true since it seems the libraries in Slicer3 are in lib/ instead of bin/, where the executables are; very Win32 unfriendly :). This set of scripts will do all that for you, point to the right ITK, VTK and KWWidgets lib and bin dirs, set the TCLLIBPATH, etc. Ultimately you probably won't need that if you include all third-party libs in the repository (as links to their CVS repositories counterparts I assume).
* A set of useful setup paths scripts is now automatically created using KWWidgets macros: check your <tt>bin/</tt> directory for <tt>Slicer3SetupPaths.bat</tt> (or .sh, .csh), and source anyone of them to update your environment variables so that you can run Slicer3 executable on Win32, especially if it is built in shared lib mode. Without this script, it becomes quickly impossible to setup the <tt>PATH, TCLLIBPATH, LD_LIBRARY_PATH</tt> (which names can change depending on the OS btw) manually each time. This is especially true since it seems the libraries in Slicer3 are in stored <tt>lib/</tt> instead of the usual <tt>bin/</tt>, where the executables are stored; this works fine on Unix, but Windows will choke trying to find the DLL the executables depends on. Anyway, this set of setup scripts will do all that for you, point to the right ITK, VTK and KWWidgets lib and bin dirs, set the <tt>PATH, TCLLIBPATH</tt>, etc. Ultimately you probably won't need that if you include all third-party libs in the repository (as links to their CVS repositories counterparts I assume).
* I found no less than 3 different instances of '#define SLICER_VTK5' and two of '#define VTKSLICER_STATIC'. This should probably be unified on top.
* I found no less than 3 different instances of <tt>#define SLICER_VTK5</tt> and two of <tt>#define VTKSLICER_STATIC</tt>. This could probably be unified on top.
* We can talk about Tcl wrapping tomorrow, there is actually a macro in KWWidgets that sits on top of the macros in VTK and try to cope for various VTK versioning issues (I mentioned it in the main CMakeList.txt inside comments, and used it in Base/GUI).
* We can talk about Tcl wrapping tomorrow, but there is actually a macro in KWWidgets (<tt>KWWidgets_WRAP_TCL</tt>) that sits on top of the Tcl wrapping macros in VTK and try to cope for various VTK versioning issues (I mentioned it in the main <tt>CMakeList.txt</tt> inside comments, and used it now in <tt>Base/GUI</tt>).
* I ran Slicer3.exe, and a lot of warnings are issued because the pipeline is not setup correctly.  
* I ran Slicer3.exe, and a lot of warnings are issued because the pipeline is not setup correctly before you load something.  
* Slicer crashed on exit, I fixed one crash point, and this is something interesting actually: the Slicer app was listening to the ModifiedEvent of one of its UI components in order to update itself; the trick here is that nothing prevents a class destructor to do something like this: <tt>this->SetFooBar(NULL);</tt>, which deallocates the <tt>FooBar</tt> string if it was defined using the very common vtkSetStringMacro and vtkGetStringMacro macros in its header. What this means is that you may be called back during an object destruction! Now as long as you carefully check that the object still exist (which is what I fixed, and that should be a good practice anyway), you should be OK. I would recommend listening to more specific events though, and/or ask me to add them for you in KWWidgets.
* Slicer crashed on exit, I fixed one crash-point, and this is something interesting actually: the Slicer app was listening to the <tt>ModifiedEvent</tt> of one of its UI components in order to update itself; the issue to keep in mind here is that nothing prevents a class destructor to do something like this:  
* Slicer3.exe is still crashing in ~vtkSlicerApplicationGUI because this->KWApplication is probably deleted multiple times. The whole KWApplication attribute that is very confusing in the current design, since it is found in different classes *and* duplicated: remember that any vtkKWObject *already* have a pointer to a vtkKWApplication object, i.e. you are either duplicating it, or using two at the same time, and if not synced properly, bad things will happen :) In our case here, there has probably something to do with ref counting, etc. I would *strongly* recommend you use the one already in KWWidgets, that can be set using vtkObject::SetApplication, and which is ref-counted properly. I can help you with that.
  this->SetFooBar(NULL);
which deallocates the <tt>FooBar</tt> string if it was defined using the very common pattern:
  vtkSetStringMacro(FooBar);
  vtkGetStringMacro(FooBar);
in the class header. Now remember that <tt>SetFooBar()</tt> *will* trigger a <tt>ModifiedEvent</tt>. What this means is that you may be called back during an object destruction! Now as long as you carefully check that the object still exists (which is what I fixed, and that should be a good practice anyway), you should be OK. I would recommend listening to more specific events though, and/or ask me to add them for you in KWWidgets.
* Slicer3.exe is still crashing in <tt>~vtkSlicerApplicationGUI</tt> because <tt>this->KWApplication</tt> is probably deleted multiple times. The whole <tt>KWApplication</tt> attribute is very confusing to me in the current design, since it is found, declared and probably duplicated as an attribute to different classes: remember that any <tt>vtkKWObject</tt> already have a pointer to a <tt>vtkKWApplication</tt> object (<tt>this->Application</tt>), i.e. the design is either duplicating it, or using two at the same time, and if not synced properly, bad things will happen :) In our case here, this has probably something to do with ref counting, etc. I would strongly recommend you use the one already in KWWidgets's <tt>vtkKWObject</tt>, which can be set using the <tt>vtkKWObject::SetApplication()</tt> method, and which is ref-counted properly. I can help you with that.


=> OK, I have not much time to fix more crashes, but here are few more things I noticed. I'm sure you guys are aware of them, but I'm just trying to give some constructive feedback regarding what the source tree:
=> OK, I have not much time to fix more crashes, but here are few more things I noticed. I'm sure you guys are aware of them, but I'm just trying to give some constructive feedback regarding the source tree:


* Comments. Comments. Comments. My personal opinion is that it is really important that the code is commented *as soon as possible*. Right now a large number of methods are undocumented, which makes it really tricky to follow (I think somebody should be able to get a good grasp of the architecture from the header files, which I was unable to do at the moment). I would definitely recommend grouping and commenting methods together to avoid blocks of unrelated and undocumented methods like:
* Comments. Comments. Comments. My personal opinion is that it is really important that the code is commented *as soon as possible*. Right now a large number of methods in <tt>Base</tt> are undocumented, making it really tricky to follow; I think somebody should be able to get a good grasp of the architecture from the header files, which I was unable to do at the moment. I would definitely recommend grouping and commenting methods together to avoid blocks of unrelated and undocumented methods like:
     // GUI FUNCTIONS:
     // GUI FUNCTIONS:
     virtual int BuildGUI ( vtkSlicerApplicationGUI *app ) { return 1; }
     virtual int BuildGUI ( vtkSlicerApplicationGUI *app ) { return 1; }
Line 21: Line 26:
     virtual void SetKWApplication ( vtkKWApplication *app ) ;
     virtual void SetKWApplication ( vtkKWApplication *app ) ;
     vtkKWApplication *GetKWApplication ( );
     vtkKWApplication *GetKWApplication ( );
Also remember that grouping is really important if you are planning to create documentation automatically using Doxygen for example.
Also remember that grouping is paramount if you are planning to create documentation automatically using Doxygen.


* Style. Style. Style. It is probably equally important to adopt a consistent coding style. I'll let you guys decide which one, but since most classes inherit from VTK, I would strongly recommend using the VTK style, i.e. 2 spaces indentation, newline before curly braces, avoid one liners (dangerous), etc. I.e. this code:
* Style. Style. Style. Another thing I've noticed in my years at Kitware, it is probably equally important to adopt a consistent coding style. I'll let you guys decide which one, but since most classes inherit from VTK, I would strongly recommend using the VTK style, i.e. 2 spaces indentation, newline before curly braces, avoid one-liners (pretty dangerous), etc. I.e. this code:
     if ( this->LogicCommand )
     if ( this->LogicCommand )
         this->LogicCommand->Delete ( );
         this->LogicCommand->Delete ( );
Line 31: Line 36:
     this->LogicCommand->Delete();
     this->LogicCommand->Delete();
     }
     }
We have emacs code to do that automatically for you, if you are interested.
We have emacs and vi modes to do that automatically for you, if you are interested.


* Forward declaration. It's actually a very good practice to include as few as possible dependent headers in a class declaration. I.e., if you do not explicitly refer to the vtkCollection API in your class header, you want to use
* Forward declaration. I think it's actually a very good practice to include as few as possible dependent headers in a class declaration, i.e., if you do not explicitly refer to the <tt>vtkCollection</tt> API in your class header for example, you really want to use:
   class vtkCollection;
   class vtkCollection;
instead of
instead of:
   #include "vtkCollection.h"
   #include "vtkCollection.h"
A counterpart to this is to try to avoid inlining in the header file as much as possible, unless it's only one line of code. Inlining in declaration files makes them so much longer and distract from the API, i.e. you should only be exposed to the public interface, not know what is going on under the hood. Inlining also creates a lot of dependences. For example, in vtkSlicerApplicationLogic.h, you probably want to avoid:
A counterpart to this is to try to avoid inlining code in the header file as much as possible (unless it's only one line of code, really). Inlining in declaration files makes them so much longer and distract from a clean API; you should probably only be exposed to the public interface of a class, not what is going on under the hood. Inlining also creates a lot of dependencies. For example, in <tt>vtkSlicerApplicationLogic.h</tt>, you probably want to avoid:
   void Connect (const char *URL) {
   void Connect (const char *URL) {
     if (this->MRMLScene)
     if (this->MRMLScene)
Line 45: Line 50:
       }
       }
   };
   };
Now this is not just fancy :) We actually cleaned up VTK 4.0 and remove all extraneous #include that way, and this sped up the compilation *tremendously* as well as avoided a lot of dependencies checking. As you probably know, moving the above lines to the definition file (.cxx) will not harm your performance at all here (and virtual functions are used so much in VTK that inlining is actually not used very often).
Now believe me, this is not just fancy :) We actually cleaned up VTK 4.0 and remove all extraneous #include that way, and this sped up the compilation process *tremendously* as well as enabled us to avoid a lot of dependencies checking. This is going to be *very* true in a project that uses a lot of large external third-party libs like VTK, ITK and KWWidgets (if you are wondering what CMake is doing each time you compile, well it's checking the dependencies). As you probably know, moving the above lines to the definition file (.cxx) will not harm your performance; virtual functions are used so much in VTK that inlining is actually not used very often.


* I would recommend using STL instead of the (aging) vtkCollection for all your internal container needs :)
* I would recommend using STL instead of the (aging) vtkCollection for all your internal container needs :)
Thanks !

Latest revision as of 04:14, 23 February 2006

A few notes about Slicer 3.0

  • I updated the CMakeLists.txt to match the changes in KWWidgets
  • Slicer3 would not compile for Windows in shared mode, for the dll export symbol was missing in the headers in Base/GUI classes. I noticed there was a VTK_SLICER_BASE_LOGIC_EXPORT inside Base/Logic, and a VTK_SLICER_BASE_EXPORT in the parent dir, so I introduced a new one in Base/GUI (VTK_SLICER_BASE_GUI_EXPORT). You may want to make sure you need a unique one for each sub-libs in Base/ (this is the case in VTK for each of its packages for example, say VTK_HYBRID_EXPORT for the Hybrid/ subdir), or just a global one (which was the case some time ago in VTK, with a unique VTK_EXPORT for the whole tree). Also, you do not need to prefix the export symbol with VTK_ anymore, SLICER_BASE_EXPORT is just fine, I updated the wrappers some time ago to support it :) It's OK to disrespect VTK that way :)
  • A set of useful setup paths scripts is now automatically created using KWWidgets macros: check your bin/ directory for Slicer3SetupPaths.bat (or .sh, .csh), and source anyone of them to update your environment variables so that you can run Slicer3 executable on Win32, especially if it is built in shared lib mode. Without this script, it becomes quickly impossible to setup the PATH, TCLLIBPATH, LD_LIBRARY_PATH (which names can change depending on the OS btw) manually each time. This is especially true since it seems the libraries in Slicer3 are in stored lib/ instead of the usual bin/, where the executables are stored; this works fine on Unix, but Windows will choke trying to find the DLL the executables depends on. Anyway, this set of setup scripts will do all that for you, point to the right ITK, VTK and KWWidgets lib and bin dirs, set the PATH, TCLLIBPATH, etc. Ultimately you probably won't need that if you include all third-party libs in the repository (as links to their CVS repositories counterparts I assume).
  • I found no less than 3 different instances of #define SLICER_VTK5 and two of #define VTKSLICER_STATIC. This could probably be unified on top.
  • We can talk about Tcl wrapping tomorrow, but there is actually a macro in KWWidgets (KWWidgets_WRAP_TCL) that sits on top of the Tcl wrapping macros in VTK and try to cope for various VTK versioning issues (I mentioned it in the main CMakeList.txt inside comments, and used it now in Base/GUI).
  • I ran Slicer3.exe, and a lot of warnings are issued because the pipeline is not setup correctly before you load something.
  • Slicer crashed on exit, I fixed one crash-point, and this is something interesting actually: the Slicer app was listening to the ModifiedEvent of one of its UI components in order to update itself; the issue to keep in mind here is that nothing prevents a class destructor to do something like this:
 this->SetFooBar(NULL);

which deallocates the FooBar string if it was defined using the very common pattern:

 vtkSetStringMacro(FooBar);
 vtkGetStringMacro(FooBar); 

in the class header. Now remember that SetFooBar() *will* trigger a ModifiedEvent. What this means is that you may be called back during an object destruction! Now as long as you carefully check that the object still exists (which is what I fixed, and that should be a good practice anyway), you should be OK. I would recommend listening to more specific events though, and/or ask me to add them for you in KWWidgets.

  • Slicer3.exe is still crashing in ~vtkSlicerApplicationGUI because this->KWApplication is probably deleted multiple times. The whole KWApplication attribute is very confusing to me in the current design, since it is found, declared and probably duplicated as an attribute to different classes: remember that any vtkKWObject already have a pointer to a vtkKWApplication object (this->Application), i.e. the design is either duplicating it, or using two at the same time, and if not synced properly, bad things will happen :) In our case here, this has probably something to do with ref counting, etc. I would strongly recommend you use the one already in KWWidgets's vtkKWObject, which can be set using the vtkKWObject::SetApplication() method, and which is ref-counted properly. I can help you with that.

=> OK, I have not much time to fix more crashes, but here are few more things I noticed. I'm sure you guys are aware of them, but I'm just trying to give some constructive feedback regarding the source tree:

  • Comments. Comments. Comments. My personal opinion is that it is really important that the code is commented *as soon as possible*. Right now a large number of methods in Base are undocumented, making it really tricky to follow; I think somebody should be able to get a good grasp of the architecture from the header files, which I was unable to do at the moment. I would definitely recommend grouping and commenting methods together to avoid blocks of unrelated and undocumented methods like:
   // GUI FUNCTIONS:
   virtual int BuildGUI ( vtkSlicerApplicationGUI *app ) { return 1; }
   virtual void SetWindow ( vtkKWWindowBase *win );
   vtkKWWindowBase *GetWindow ( );
   virtual void SetSlicerApplication ( vtkSlicerApplicationGUI *app );
   vtkSlicerApplicationGUI *GetSlicerApplication ( );
   virtual void SetKWApplication ( vtkKWApplication *app ) ;
   vtkKWApplication *GetKWApplication ( );

Also remember that grouping is paramount if you are planning to create documentation automatically using Doxygen.

  • Style. Style. Style. Another thing I've noticed in my years at Kitware, it is probably equally important to adopt a consistent coding style. I'll let you guys decide which one, but since most classes inherit from VTK, I would strongly recommend using the VTK style, i.e. 2 spaces indentation, newline before curly braces, avoid one-liners (pretty dangerous), etc. I.e. this code:
   if ( this->LogicCommand )
       this->LogicCommand->Delete ( );

is in VTK:

 if (this->LogicCommand) 
   {
   this->LogicCommand->Delete();
   }

We have emacs and vi modes to do that automatically for you, if you are interested.

  • Forward declaration. I think it's actually a very good practice to include as few as possible dependent headers in a class declaration, i.e., if you do not explicitly refer to the vtkCollection API in your class header for example, you really want to use:
  class vtkCollection;

instead of:

  #include "vtkCollection.h"

A counterpart to this is to try to avoid inlining code in the header file as much as possible (unless it's only one line of code, really). Inlining in declaration files makes them so much longer and distract from a clean API; you should probably only be exposed to the public interface of a class, not what is going on under the hood. Inlining also creates a lot of dependencies. For example, in vtkSlicerApplicationLogic.h, you probably want to avoid:

 void Connect (const char *URL) {
   if (this->MRMLScene)
     {
     this->MRMLScene->SetURL(URL);
     this->MRMLScene->Connect();
     }
 };

Now believe me, this is not just fancy :) We actually cleaned up VTK 4.0 and remove all extraneous #include that way, and this sped up the compilation process *tremendously* as well as enabled us to avoid a lot of dependencies checking. This is going to be *very* true in a project that uses a lot of large external third-party libs like VTK, ITK and KWWidgets (if you are wondering what CMake is doing each time you compile, well it's checking the dependencies). As you probably know, moving the above lines to the definition file (.cxx) will not harm your performance; virtual functions are used so much in VTK that inlining is actually not used very often.

  • I would recommend using STL instead of the (aging) vtkCollection for all your internal container needs :)

Thanks !