顯示具有 Programming 標籤的文章。 顯示所有文章
顯示具有 Programming 標籤的文章。 顯示所有文章

2009-06-18

A Bitmapped Programming Font: peep

Download this font from here.

Get a list of all Tcl commands by Tcl_InfoObjCmd

I am combining Tcl library and GNU readline to provide a Tcl line editing environment. One of the problems I have is the command completion of GNU readline library. In order to do command completion, you have to provide a list of currently available Tcl commands to readline. I know it is already there in Tcl language, i.e, info commands. But, it also dumps the list to the console, :(. Imaging that when you press TAB key, the program prints out all commands first and then the commands matched your request. This is not acceptable. This also means calling Tcl_Eval("info commands") to get result back to process is not an option to me. With hours of searching over Tcl source code, I can't find a public C functionhat does the job. But, fortunately, an internal function Tcl_InfoObjCmd seems to be the cure (Well, its signature may be changed in the future, I know!!) Just use following code to get result,
   Tcl_Obj* objPtr[2];
   objPtr[0] = Tcl_NewStringObj( "info", -1 );
   objPtr[1] = Tcl_NewStringObj( "commands", -1 );

   // I know this function is internal to Tcl, but it seems no other C
   // functions to get all command names out of Tcl.
   Tcl_InfoObjCmd( 0, d_interp, 2, objPtr );

   return getResult().split( " ", QString::SkipEmptyParts );

2009-06-11

Auto-execute shell built-ins or executables in Tcl interactive mode

When I was comparing my customized Tcl line editing environment with the one provided by Tcl_Main(), I figured that one thing Tcl_Main has and my implementation hasn't is the auto-execute shell built-ins or executables. We both are calling Tcl_EvalObjEx() to evaluate commands and same commands are passed into this function on both sides. Why does my environment behave differently? After minutes of scratching heads and searching over Tcl source code, I noticed the Tcl command unknown defined in init.tcl that gets called after the command being executed is identified as non-existed in Tcl interpreter. It tries to run non-existed commands in normal shell if Tcl is at interactive mode. Tcl knows itself is in interactive mode by checking the value of a boolean variable tcl_interactive and the value of this variable is linked to the result of C call isatty(0). If you are customizing your own version of Tcl editing environment and want to execute shell built-ins automatically just like me, you probably need to tell Tcl about this interactive stuff by isatty(0).

2009-05-06

Implement your own shell mode based on Tcl_Main()

In case your clients told you that they will be very happy to see a shell mode of your application implemented because they see this mode as a productivity add (why? who knows? customers always rule!)

Your application already has Tcl interpreter embedded. Why not just take advantage of the code written by Tcl developers? Using it will save us a lot of efforts. After all, you're hired to solve problems, not to reinvent the wheels. But, how?

Tcl has defined a function Tcl_Main() which is the entry function of tclsh. Now, take a look at the generic/tclMain.c in Tcl source. It is called as,

    Tcl_Main(argc, argv, AppInit /* init function */);

and this ~AppInit~ must be declared as

    int AppInit(Tcl_Interp* interp);

In my implementation, this AppInit function is responsible for

  1. Set Tcl library paths pointed by tcl variable tcl_library.
  2. Hack tcl command /history/ so that every time a Tcl command is executed at the shell, it is logged at log file (See definition of Tcl_RecordAndEvalObj() in generic/tclHistory.c). The hack should look something like,
    if {[auto_load ::history]} {#this is why library_path must be specified
        rename ::tcl::HistAdd ::Init::OldHistAdd
    }
    proc ::tcl::HistAdd {command {exec {}}} {
       #
       ...
    
       # my log command
       if {[catch {interp invokehidden {} _log_command_ --cmd [string trim $command]}] != 0} {
           puts "# ERROR: Command '[string trim $command]' is not logged"
           return {}
       }
       #
    }
    
    You should then provide a hidden command _log_command_ for logging.
  3. Register exit handler by Tcl_CreateExitHandler() for cleanup.
  4. Source my initialization scripts.
  5. Register my commands in Tcl interpreter.
  6. Change prompt string.
    set tcl_prompt1 {puts -nonewline stdout {my_shell> }};
    

2009-05-01

Wrap Tcl_ObjCommand in an object-oriented way

People know that Tcl is widely used in the world. It uses C-like syntax and provides Tcl/C binding. For people who know C and want their applications to support script, Tcl is a good solution.

Tcl is written in C. If a Tcl interpreter is required to be embedded in the application and you want to write Tcl command in C/C++, we usually need to do something that makes Tcl a bit object-oriented. How to create an object-oriented Tcl language is not my intention. What I am trying to do is to wrap Tcl in a way that writing of Tcl commands can be more object-oriented (maybe you can see it as a pattern). And if you know any better way, please let me know.

If we are required to create a command called run_me in Tcl interpreter and it is kinda expensive to write this command in tcl script, we can write it in C/C++. I am sure we all agree to this. The internal work is that an associated C function gets called when run_me is executed in tclsh.

First, a Tcl_CreateObjCommand can be used to create this Tcl to C binding and it needs parameters as follows:

Tcl_CreateObjCommand(interp, /* Tcl_Interp* */
                    "run_me",    /* const char*: Tcl command name */
                    &my_run_me_c_func,    /* C function to be called */
                    0, 0 );

In the simplest form, you can just add my_run_me_c_func at global namespace. Of course, it's not a good enough solution for C++ developer because you may add too many functions in global namespace. It shouldn't surprise you that I use a class hierarchy to manage these C bindings. And it shouldn't surprise you again that I apply Command Pattern in this scenario. The base class of the hierarchy may look like,

class TclCommand {
public:
    virtual ~TclCommand() {}
    int execute( ClientData clientData,
                 Tcl_Interp* interp,
                 int objc,
                 Tcl_Obj* const objv[] ) {
        // print help if having -h option

        // syntax checking by is_valid()
 
        // everything is fine, call do_execute()

        return TCL_OK;
    }
protected:
    TclCommand();
    virtual int  do_execute( /* necessary arguments */ ) = 0;
    virtual bool is_valid( /* arguments from command line */ ) = 0;
    virtual void help( /* necessary arguments */ ) = 0;
};

class RunMeTclCommand : public TclCommand {
private:
    // Override pure virtual functions in base class.
};

For different Tcl commands, different TclCommand class should be added in this class hierarchy. The problem is how to let Tcl know run_me and RunMeTclCommand are connected?

First, every Tcl command is linked to a local static function TclCommandLinker and it dispatches call to appropriate TclCommand object. Its content may look like

static int
TclCommandLinker( ClientData clientData,
               Tcl_Interp* interp,
               int objc,
               Tcl_Obj* const objv[] )
{
    // ...
    int length = 0;
    TclCommand* cmd = TclCommandMap::instance()->getMapping(
           Tcl_GetStringFromObj( objv[0], &length ) );
    if ( cmd ) {
       return cmd->execute( clientData, interp, objc, objv );
    }
    return TCL_ERROR;
}

You can see from above that a singleton object TclCommandMap used here to get proper TclCommand mapping. This object can be implemented by std::map as,

class TclCommandMap {
public:
    static TclCommandMap* instance();
    void addMapping(const std::string& name, TclCommand* cmd);
    Command* findMapping(const std::string& name);
    // ...
};

Second, Tcl knows this TclCommandLinker through,

Tcl_CreateObjCommand( interp,
                    "run_me",
                    TclCommandLinker,
                    0, 0 );

Finally, when a Tcl command is registered into Tcl interpreter,

// ...
TclCommandMap::instance()->addMapping("run_me",
                                    new RunMeTclCommand( /* parameters */ );
// ...

That's it. We're done.

2009-04-30

Use QSettings

Normally, different applications have different ways to handle user preferences. For instance, it may be natural to choose Tcl script to save preferences for those applications that support Tcl scripting. But some specific settings, like window geometries opened last time, we don't want to trouble users to modify manually. We can choose whatever we feel convenient and they don't care about these settings.

In this case, the best way to handle this is using QSettings. In Unix platforms, QSettings put setting files under ~/.config according to application name and the organization that produces it. You may see directory structure like this,

[yenliangl@halo]:~/.config > ll total 13 drwx------ 2 yenliangl staff 512 May 16 2007 autostart/ drwxr-x--- 2 yenliangl staff 512 Apr 7 19:30 fixit/ drwx------ 2 yenliangl staff 512 Apr 30 12:28 gtk-2.0/ drwxr-x--- 2 yenliangl staff 512 Jul 17 2008 menus/ drwxr-x--- 2 yenliangl staff 512 Dec 24 16:43 Synopsys/ drwxr-xr-x 2 yenliangl staff 512 Jul 10 2007 Trolltech/ -rw------- 1 yenliangl staff 5769 Apr 22 13:47 Trolltech.conf -rw-r----- 1 yenliangl staff 242 Apr 13 18:38 Unknown Organization.conf
All Qt-related settings are located at Trolltech/ or in Trolltech.conf. How do we use QSettings? First, we let QSettings know our organization and product by,
   QCoreApplication::setOrganizationName( "SomeCompany" );
 QCoreApplication::setOrganizationDomain( "somecompany.com" );
 QCoreApplication::setApplicationName( "MyTool" );
and then, we have two private methods defined in our MainWindow class to write/read settings,
class MainWindow : public QMainWindow {
Q_OBJECT
public:
void closeEvent(QCloseEvent* e);
// ...

private:
void read_settings();
void write_settings();
// ...
};
In MainWindow::MainWindow(), we add a call to read_settings(),
MainWindow::MainWindow( /* ... */ )
{
// ...
read_settings();
}
and add a call to write_settings() as we exit application,
void MainWindow::closeEvent(QCloseEvent* e)
{
 // ...
 write_settings();
}
The definitions of read_settings() and write_settings() should look like,
void MainWindow::read_settings()
{
  QSettings settings;
  settings.beginGroup( "MainWindow" );
  restoreState( settings.value( "wm_state" ).toByteArray() ); // 可能有問題喔,如下。
  resize( settings.value( "size", QSize( 400, 400 ) ).toSize() );
  move( settings.value( "pos" ).toPoint() );
  settings.endGroup();
}

void MainWindow::write_settings()
{
  QSettings settings;
  settings.beginGroup( "MainWindow" );
  settings.setValue( "wm_state", saveState() ); // 可能有問題喔,如下。
  settings.setValue( "size", size() );
  settings.setValue( "pos", pos() );
  settings.endGroup();
}
set? not yet. If you are using QDockWidget or QToolBar in your application, we should see an error message at application run-time, saying that QDockWidget doesn't have objectName property set. From release note of Qt4, you should find,
The QMainWindow::saveState() and QMainWindow::restoreState() functions no longer fallback to using the windowTitle property when the objectName property is not set on a QToolBar or QDockWidget; this behavior was undocumented and has been removed.
It points out that you need to set objectName property manually. Simple!
myDock->setObjectName("whatever name"); // Or/And
myToolBar->setObjectName("whatever name");
Now. We're done. Run your application. You will discover your application remembers the window state and even the states of dock widgets.

2009-04-29

Qt4的安裝與編譯

在下載及安裝之前,先試著了解自己的需求。是要商業版呢?或是OpenSource版呢? 對於一般人來說下載OpenSource版應該就可以了(此外,因為Qt目前是採取LGPL的授權,根據上次研討會Qt部門頭頭的說法,只要你最後的執行檔是動態連結Qt的程式庫,就可以開發付費的軟體)。 接著你還要決定你是要下載完整的SDK(以執行檔型式)或是只要必要的程式(以原始碼形式)就好。對於喜歡使用IDE來開發軟體的人來說,倒是可以下載完整的SDK,直接試試看裡面的Qt Creator這個IDE(當然,你也可以下載原始碼編譯出來Qt Creator))。在台灣Nokia辦的研討會中,這個IDE倒是受到相當多的注目。但是,個人淺見是沒有一定要用他的必要。一般的編輯軟體就很足夠了。而以我現在用的Ubuntu 9.04系統,Qt 4.5.0已經包含在套件庫裡,不必自己手動編譯。但是,若是想要試試看新的4.5.1的版本,就可以在這裡下載原始碼來編譯。 將壓縮檔解開後,進入目錄,執行 [josh@halo:~/qt-x11-opensource-src-4.5.0 > ./configure --help ,來看看編譯時可以用的選項。我個人大約會注意的有這幾個,
  1. -prefix:在你想將Qt安裝在特定的目錄時,可以用這個選項。通常,在一般的開發環境中,所有的開發工具會安裝在同一父目錄下,再由Make rules指定每一個Release應該使用的工具或函式庫版本。這樣的安排,主要是要避免不同開發者之間鍊結錯誤函式庫的機會。這時候,管理者就可以使用這個選項將Qt安裝在規定的目錄下。
  2. -release|-debug|-debug-and-release:是否產生偵錯版本。
  3. -shared|-static:這個選項很有意思。之前提過在LGPL下,Qt的函式庫必須被動態鍊結,也就是你的程式必須動態開啟Qt的.so函式庫。而-shared這個選項,就是編譯出一套Qt的.so函式庫。相反地,當你希望Qt的函式庫是整個包含在你的執行檔時,就可以用-static來編譯出Qt的Archived函式庫(.a),之後再連結進執行檔。這個情形下,使用ldd,就不會列出Qt的任何.so檔。
  4. -make或-nomake:通常,你可以經由這個選項指定要編譯的模組。我通常覺得不需要編譯examples和demos兩項,所以我可以把他們加在-nomake裡。
決定好參數之後,就可以開始編繹了, [josh@halo:/tmp/qt-x11-opensource-src-4.5.0 > ./configure -prefix /home/josh/qt/4.5.0 -shared -debug-and-release -nomake "examples demos translation" [josh@halo:/tmp/qt-x11-opensource-src-4.5.0 > gmake; gmake install 以我的機器,大概15-30分鐘,編譯就完成了。接著,將Qt的環境加入系統中(以bash為例), export QTDIR=/home/josh/qt/4.5.0 export PATH= "$QTDIR/bin:$PATH" export LD_LIBRARY_PATH= $QTDIR/lib:$LD_LIBRARY_PATH 現在,就可以試著執行Qt內付的工具,如assistant或是qtconfig等。

2009-03-01

Programming fonts

There are many sites on Internet recommending many good fixed-width fonts for daily programming. Of all these recommended fonts, I personally like BPmono. It is clear, has slashed zero.

But, changes are the nature of human being. Sometimes when I feel bored about BPmono, I switch to another interesting font Monofur. It's cute and makes you feel good, at least (Study shows happy people are more creative). LOL.

2008-10-01

GNOME開發文件

在寫Maemo程式時,一直苦於GNOME環境沒有像Qt的assistant一樣方便的程式庫文件程式。今天,無意間發現居然有devhelp這玩意。顧名思義,這東西是寫GNOME平台程式的Help程式。他的功用和assistant一樣,將程式庫的說明文件整理好,包在一個程式中,方便使用者查詢。

2008-09-30

Integrating applications into Maemo Application Framework

Maemo .desktop file
  1. To make an application visible in maemo Task navigator, a desktop file is needed for the application.
  2. /usr/share/applications/hildon/[application].desktop
  3. Use af-sb-init.sh restart to restart GUI again in order for the new entry to be seen in the task navigator.
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Name=Example libOSSO <=== the name of the application Exec=/usr/bin/example_libosso ;; <== binary to launch X-Osso-Service=org.maemo.example_libosso ;; <=== D-BUS service name Icon=qgn_list_gene_default_app MimeType=application/x-example;
D-Bus Service File
  • Only one instance of a D-BUS service can be run simultaneously, guaranteeing that each application is running only once.
  • /usr/share/dbus-1/services/[application].service
  • use af-sb-init.sh restart before D-BUS daemon recognizes it.

[D-BUS Service]
Name=org.maemo.example_libosso # D-Bus service name (has to be unique)
Exec=/usr/bin/example_libosso # binary to launch

LibOSSO Library

Maemo initialization

All maemo applications need to be initialized correctly, or they will not work as expected.

Initializing application is performed with osso_initialize() function.
  1. Connects to D-Bus session bus and system bus.
  2. Should only be called once in the application.
  3. The structure of type osso_context_t type that is returned should be stored for later use.


osso_context_t* /* shoud be stored for later use */
osso_initialize(const gchar *application,/* D-BUS name */
const gchar* version, /* application version */
gboolean activation, /* TURE: application binary has been launched by the D-Bus
GMainContext *context /* GLib main-loop context to connect to */
)

osso_deinitialize() should be called to close the message bus connection and free all the memory allocated by the library.
void osso_deinitialize(osso_context_t* osso);

Remote Process Messages

System wide messages in maemo platform are handled with D-BUS system messaging, which is a Remote Process Communication (RPC) method.

osso_rpc_set_cb_f(...);
gint dbus_message_handler(const gchar* interface,
const gchar* method,
GArray* arguments,
gpointer data,
sso_rpc_t* retval);
osso_rpc_run(osso_context,
const gchar* service,
const gchar* object,
const gchar* interface,
const gchar* message,
...);

The receiving application does not even need to be started: D-BUS can automatically start the application based on its service file, and then pass the message to it!.
Hardware State Messages

Maemo applications can connect to listen the system D-BUS messages, like "battery low" and "shutdown".When these messages are received, the application may want to ask the user to save files that are open, or react however wanted.

void hw_event_handler(osso_hw_state_t* state, gpointer data);
state->memory_low_ind
state->system_inactivity_ind
osso_hw_set_event_cb( appdata->osso_context, NULL, hw_event_handler, (gpointer)appdata );

These hardware events are not sent to the SDK, testing them is only possible in maemo device.
System Exit Message


void exit_event_handler(gboolean die_now, goiinter data);
osso_application_set_exit_cb(appdata->osso_context,
exit_event_handler,
(gpointer)appdata);

whenever the system needs to close an application for any reason, exit_event_handler will be performed gracefully.

Information on how to implement state saving can be found in LibOSSO API document.

2008-09-29

Install Maemo Dialo Development Platform 4.1.1

The installation of the Maemo Development Platform contains: Scratchbox,Maemo SDK and Nokia binaries. These three steps can be automated by the script written by Nokia or the community.

Install Scratchbox
Use,
$ wget http://repository.maemo.org/stable/diablo/maemo-scratchbox-install_4.1.1.sh
$ chmod a+x ./maemo-scratchbox-install_4.1.1.sh
$ sudo ./maemo-scratchbox-install_4.1.1.sh
If the script comlains something about vdso_enabled or min_addr, insert the following settings into your /etc/sysctl.conf. Like,
vm.vdso_enabled = 0
vm.mmap_min_addr = 4096
net.ipv4.ip_local_port_range = 1024 65535

Then, execute,
sudo sysctl -p

Install Maemo SDK
Use,

$ wget http://repository.maemo.org/stable/diablo/maemo-sdk-install_4.1.1.sh
$ chmod u+x maemo-sdk-install_4.1.1.sh
$ sudo ./maemo-sdk-install_4.1.1.sh

Install Nokia Binaries
Use,
$ wget http://repository.maemo.org/stable/diablo/maemo-sdk-nokia-binaries_4.1.1.sh
$ chmod +x maemo-sdk-nokia-binaries_4.1.1.sh
$ sh maemo-sdk-nokia-binaries_4.1.1.sh
Then, log into Scratchbox system, execute,
$ /scratchbox/login
[sbox-DIABLO_ARMEL: ~] > fakeroot apt-get install maemo-explicit

Install Virtual X11 server (XEPHYR)
Before we can run applications from SDK, the Xephyr X11 Server has to be installed first.
$ sudo apt-get install xserver-xephyr

2008-09-28

Emacsclient in Scratchbox

I am using Emacs to program both at work and home. When I found I can't use Emacsclient inside ScratchBox, although, it is claimed OK in the documentation, I felt a bit surprised. The error reads,
[sbox-DIABLO_ARMEL: /tmp] > emacsclient emacsclient-1.c
Can't stat /tmp/esrv1000-yenliang-laptop
[sbox-DIABLO_ARMEL: /tmp] > ls /tmp/esrv1000-yenliang-laptop
ls: /tmp/esrv1000-yenliang-laptop: No such file or directory


Obviously, the socket file /tmp/esrv1000-yenliang-laptop is not created by Emacs server. I looked into the source code of Emacsclient shipped from Emacs and found that the socket file it is looking for is /tmp/emacs1000/server as indicated by the code below.

sprintf(socket_name, "/tmp/emacs%d/%s",
(int)geteuid(), server_name);


But, the code in ScratchBox version is,

sprintf(server.sun_path, "/tmp/eserv%d-%s",
(int)geteuid(), system_name);

I think if I change the socket file path above to match the one used by Emacs server and compiled a new emacsclient, it should work. And it does work.



The screenshot above says it works like a charm, though, the Emacs started the server is version 22.2, not CVS version. The emacsclient fails to connet to the server created by CVS version. My guess is that the CVS version returns the different ID as the client initializes the connection.

Another way you can try is to create a symbolic link at the home directory of the host pointing to the home directory in the ScratchBox system.
ln -s /scratchbox/users/$USER/home/$USER sbox

2008-09-24

HelloWorld example in Hildon Desktop

After a long installation of ScratchBox and Hildon SDK, everyone would like to see how Hildon Desktop looks like. The first thing we should do is to start a virtual X server with a display number different than the host, here we use :2.
[yenlaingl@yenliangl-laptop:~] Xephyr :2 -host-cursor -screen 800x480x16 -dpi 96 -ac -extension Composite
The second thing to do is to redirect the DISPLAY of all X clients in sbox to the :2
export DISPLAY=:2

The third thing we should do is to start up the Hildon Application Framework.
[sbox-DIABLO_ARMEL: ~/workspace]: af-sb-init.sh start

You should see the Hildon Desktop shown inside the window of the Xephyr, such as,



And now, time to invoke the HelloWorld example.
[sbox-DIABLO_ARMEL: ~/workspace]: run-standalone.sh ./hello



Cool! In fact, I've had a dilemma choosing OpenMoko or Nokia Internet Tablet to be my off-work hobby. I did try to install OpenMoko SDK, but honestly, it's not quite intuitive and hard to make it work. Inversely, Diablo is very easy to install and get started.

How to use boost::disjoint_sets

The disjoint set is used in MST and many other algorithms. The boost library has implemented a convenient for us. It can be used like the following example,
typedef std::vector<SizeType> RankVector;
typedef RankVector::iterator RankRAIter;
typedef std::vector<VertexDescriptor> ParentVector;
typedef ParentVector::iterator ParentRAIter;

typedef boost::iterator_property_map<RankRAIter, IndexMap,
std::iterator_traits<RankRAIter>::value_type,
std::iterator_traits<RankRAIter>::reference> RankMap;

typedef boost::iterator_property_map<ParentRAIter, IndexMap,
std::iterator_traits<ParentRAIter>::value_type,
std::iterator_traits<ParentRAIter>::reference> ParentMap;

RankVector ranks( d_numOfVertices );
ParentVector parents( d_numOfVertices );
boost::disjoint_sets<RankMap, ParentMap> dset(
boost::make_iterator_property_map(
anks.begin(), boost::get( boost::vertex_index, g ), ranks[0] ),
boost::make_iterator_property_map(
parents.begin(), boost::get( boost::vertex_index, g ),
parents[0] )
);


After the disjoint set dset is construct, we can use
dset.make_set( u ); // make u a set
dset.link(u, v); // u and v are belonging to the same set.
dset.find_set( u ); // find the set owning u. A representative of the set is returned

2008-09-23

Write Hildon Control Panel Applet

I studied the document describing how to develop a desktop applet for Hildon desktop as well as GNOME. Before this, I thought writing an applet is difficult, at least cumbersome. It is a wrong idea. The GNOME desktop has evolved many generations and developers all over the world have contributed many to it. I should dig into it and see if there is anything I can do for this before I make any judgement.



A horoscope applet that gets personal horoscope daily from websites might be useful and also a good starting point for me.

How to procude high-quality code

Reviewed chapter 5, 6 and 7 of Code Complete2 and drew things I think important in FreeMind. I would say MindMap is definitely a superb tool for everyone to organize things or even use it as a note-taking tool.

2008-09-22

This mind map briefly describes the important widgets that can be used to create applications in Nokia internet tablet. It is drawn by FreeMind by the way.

2008-09-17

The use of dladdr in OpenAccess

Today, I was wondering if I can use archive version of OpenAccess libraries when I was building the newest version of OpenAccess DM3. So, I followed the steps at the previous post to build archive versions of OpenAccess libraries. Everything worked fine at first but when I started to load the design library, OpenAccess complains about the wrong plug-ins installation path. I know this error probably come from the installation path is not correcly obtained from the experience.. After delving into the source code at oaCommonInstallDir.cpp for a while, I found out that the suspicious code is
238 InstallDir::getLibPath()
239 {
240     if (libPath.size()) {
241     return libPath.c_str();
242     }
243
244     Dl_info info = { NULL, NULL, NULL, NULL };
245
246     if (!dladdr((void*) dirSep, &info)) {
247     return libPath.c_str();
248     }
249
250     libPath = info.dli_fname;

The Dl_info filled in by dladdr has the wrong value at dli_fname field. Why??

Because all the OA libraries are in the form of archive libraries, the address of variable dirSep is located at the main executable rather than the shared object file. So, the dli_fname resolved may not be right, in fact, it is the argv[0] of the calling process. Check this out (See item 2 of section BUGS at here).
If addr lies in the main executable rather than in a shared library, the pathname returned in dli_fname may
not be correct. The pathname is taken directly from argv[0] of the calling process. When executing a program
specified by its full pathname, most shells set argv[0] to the pathname. But this is not required of shells 
or guaranteed by the operating system.
It turns out that the module oaCommon must be linked into the main executable in the form of shared object file in order for OA to generate the right installation path from dladdr. I made it so, but the experiment conducted later still shows that the OA developers don't even consider this be an use model. The oaTurboDMServer launched by the OA system still tries to load shared object files, like liboaTech.so, liboaDesign.so, etc. With all these libraries must be in the form of shared libraries, I think it's time to give up. :)