Inside GDALAllRegister之二: 自动加载驱动

代码    GetGDALDriverManager()->AutoLoadDrivers(); 包含了两部分:

首先获得GDALDriverManager的singleton对象的指针,这点之前已经说明过,采用DCLP是个错误用法,不过可以通过下面的方法规避:

永远只在main函数内部单线程调用一次GDALAllRegister, 在其他线程尚未创建之前,singleton对象已经被创建出来


然后运行void GDALDriverManager::AutoLoadDrivers() 函数。这是本次分析的主要内容。注释写的不错,很容易就理解了该函数的逻辑。

/**
 * rief Auto-load GDAL drivers from shared libraries.
 *
 * This function will automatically load drivers from shared libraries.  It
 * searches the "driver path" for .so (or .dll) files that start with the
 * prefix "gdal_X.so".  It then tries to load them and then tries to call a
 * function within them called GDALRegister_X() where the 'X' is the same as
 * the remainder of the shared library basename ('X' is case sensitive), or
 * failing that to call GDALRegisterMe().
 *
 * There are a few rules for the driver path.  If the GDAL_DRIVER_PATH
 * environment variable it set, it is taken to be a list of directories to
 * search separated by colons on UNIX, or semi-colons on Windows.  Otherwise
 * the /usr/local/lib/gdalplugins directory, and (if known) the
 * lib/gdalplugins subdirectory of the gdal home directory are searched on
 * UNIX and $(BINDIR)gdalplugins on Windows.
 */


该函数在driver path中查找文件名为gdal_X.dll或者gdal_X.so的动态库文件。如果找到,就加载该动态库,并调用其中的GDALResiter_X()函数,每隔动态库都应该实现这个函数。X代表库的名称。

driver path可以通过设置环境变量GDAL_DRIVER_PATH来获得,path之间通过,或者;分隔。 如果没有设定环境变量,就使用默认查找路径:/usr/local/lib/gdalplugins, 

并且:

1. 如果Unix下有gdal home 目录的话,还会找该目录下的lib/gdalplugins子目录,

2. 如果windows下会用$(BINDDIR)gdalplugins 作为查找路径。这里$(BINDIR)值得是当前进程所在目录。

下面是全部代码:

void GDALDriverManager::AutoLoadDrivers()

{
    char     **papszSearchPath = NULL;
    const char *pszGDAL_DRIVER_PATH = 
        CPLGetConfigOption( "GDAL_DRIVER_PATH", NULL );

/* -------------------------------------------------------------------- */
/*      Where should we look for stuff?                                 */
/* -------------------------------------------------------------------- */
    if( pszGDAL_DRIVER_PATH != NULL )
    {
#ifdef WIN32
        papszSearchPath = 
            CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ";", TRUE, FALSE );
#else
        papszSearchPath = 
            CSLTokenizeStringComplex( pszGDAL_DRIVER_PATH, ":", TRUE, FALSE );
#endif
    }
    else
    {
#ifdef GDAL_PREFIX
        papszSearchPath = CSLAddString( papszSearchPath, 
    #ifdef MACOSX_FRAMEWORK
                                        GDAL_PREFIX "/PlugIns");
    #else
                                        GDAL_PREFIX "/lib/gdalplugins" );
    #endif
#else
        char szExecPath[1024];

        if( CPLGetExecPath( szExecPath, sizeof(szExecPath) ) )
        {
            char szPluginDir[sizeof(szExecPath)+50];
            strcpy( szPluginDir, CPLGetDirname( szExecPath ) );
            strcat( szPluginDir, "\gdalplugins" );
            papszSearchPath = CSLAddString( papszSearchPath, szPluginDir );
        }
        else
        {
            papszSearchPath = CSLAddString( papszSearchPath, 
                                            "/usr/local/lib/gdalplugins" );
        }
#endif

   #ifdef MACOSX_FRAMEWORK
   #define num2str(x) str(x)
   #define str(x) #x 
     papszSearchPath = CSLAddString( papszSearchPath, 
                                     "/Library/Application Support/GDAL/"
                                     num2str(GDAL_VERSION_MAJOR) "."  
                                     num2str(GDAL_VERSION_MINOR) "/PlugIns" );
   #endif


        if( strlen(GetHome()) > 0 )
        {
            papszSearchPath = CSLAddString( papszSearchPath, 
                                  CPLFormFilename( GetHome(),
    #ifdef MACOSX_FRAMEWORK 
                                     "/Library/Application Support/GDAL/"
                                     num2str(GDAL_VERSION_MAJOR) "."  
                                     num2str(GDAL_VERSION_MINOR) "/PlugIns", NULL ) );
    #else
                                                    "lib/gdalplugins", NULL ) );
    #endif                                           
        }
    }

/* -------------------------------------------------------------------- */
/*      Scan each directory looking for files starting with gdal_       */
/* -------------------------------------------------------------------- */
    for( int iDir = 0; iDir < CSLCount(papszSearchPath); iDir++ )
    {
        char  **papszFiles = CPLReadDir( papszSearchPath[iDir] );

        for( int iFile = 0; iFile < CSLCount(papszFiles); iFile++ )
        {
            char   *pszFuncName;
            const char *pszFilename;
            const char *pszExtension = CPLGetExtension( papszFiles[iFile] );
            void   *pRegister;

#if ( defined( _WIN32 ) && ( defined(DEBUG) || defined(_DEBUG) ) )
            if( !EQUALN(papszFiles[iFile],"debug_gdal_",11) )
                continue;
#else
            if( !EQUALN(papszFiles[iFile],"gdal_",5) )
                continue;
#endif

            if( !EQUAL(pszExtension,"dll") 
                && !EQUAL(pszExtension,"so") 
                && !EQUAL(pszExtension,"dylib") )
                continue;

            pszFuncName = (char *) CPLCalloc(strlen(papszFiles[iFile])+20,1);
#if ( defined( _WIN32 ) && ( defined(DEBUG) || defined(_DEBUG) ) )
            CPLString sName = CPLGetBasename(papszFiles[iFile]) + 11;
#else
            CPLString sName = CPLGetBasename(papszFiles[iFile]) + 5;
#endif
            sprintf( pszFuncName, "GDALRegister_%s", sName.c_str() );
            pszFilename = 
                CPLFormFilename( papszSearchPath[iDir], 
                                 papszFiles[iFile], NULL );

            pRegister = CPLGetSymbol( pszFilename, pszFuncName );
            if( pRegister == NULL )
            {
                sName.toupper();
                sprintf( pszFuncName, "GDALRegister_%s", sName.c_str() );
                pRegister = CPLGetSymbol( pszFilename, pszFuncName );
                if( pRegister == NULL )
                {
                    strcpy( pszFuncName, "GDALRegisterMe" );
                    pRegister = CPLGetSymbol( pszFilename, pszFuncName );
                }
            }
            
            if( pRegister != NULL )
            {
                CPLDebug( "GDAL", "Auto register %s using %s.", 
                          pszFilename, pszFuncName );

                ((void (*)()) pRegister)();
            }

            CPLFree( pszFuncName );
        }

        CSLDestroy( papszFiles );
    }

    CSLDestroy( papszSearchPath );
}


那个查找目录树的算法算不上糟糕,不过用惯了boost和newlisp的我,看到不禁皱眉头。太不优雅了。一帮C程序员在写C++代码,循环之后还要用CSLFree和CSLDestory 释放内存,有string不用。就算不用string,也可以自己写个简单的class啊。

很明显,GDAL的源代码并没有随着C++语言的发展而进步,让我不仅想起了ACE。

调试一遍后,发现我的GDAL代码在这里没有加载任何驱动。就是我想要的结果。:)

原文地址:https://www.cnblogs.com/keanuyaoo/p/3257837.html