对于我想要的东西,有更好的API吗

Is there a better API for the thing I want

本文关键字:更好 API 我想要      更新时间:2023-10-16

考虑以下两个片段:

  1. 这就是我想要的:

数据库.h:

template<class T, class T2> class __declspec(dllexport) Database
{
protected:
    struct Impl;
    Impl *pimpl;
    virtual int GetTableListFromDb(std::vector<T2> &errorMsg) = 0;
public:
    virtual ~Database() = 0;
    Impl &GetTableVector() { return *pimpl; };
    virtual int Connect(T *selectedDSN, std::vector<T2> &errorMsg) = 0;
    virtual int Disconnect(std::vector<T2> &errorMsg) = 0;
};
template<class T, class T2> struct Database<T, T2>::Impl
{
    std::vector<Table> m_tables;
};
template<class T, class T2> Database<T, T2>::~Database()
{
    delete pimpl;
}

在sqlite_db.h:中

template <class T, class T2> class __declspec(dllexport) SQLiteDatabase : public Database<T, T2>
{
public:
    SQLiteDatabase();
    virtual ~SQLiteDatabase();
    virtual int Connect(T *selectedDSN, std::vector<T2> &errorMsg);
    virtual int Disconnect(std::vector<T2> &errorMsg);
protected:
    void GetErrorMessage(int code, T2 &errorMsg);
    virtual int GetTableListFromDb(std::vector<T2> &errorMsg);
private:
    sqlite3 *m_db;
};

在odbc_db.h:中

template<class T, class T2> class __declspec(dllexport) ODBCDatabase : public Database<T, T2>
{
public:
    ODBCDatabase();
    virtual ~ODBCDatabase();
    virtual int Connect(T *selectedDSN, std::vector<T2 *> &errorMsg);
    virtual int Disconnect(std::vector<T2> &errorMsg);
protected:
    int GetErrorMessage(std::vector<T> &errorMsg);
    virtual int GetTableListFromDb(std::vector<T2> &errorMsg);
private:
    SQLHENV m_env;
    SQLHDBC m_hdbc;
    SQLHSTMT m_hstmt;
    SQLHWND m_handle;
};

以及用法-在一些dll中:

extern "C" __declspec(dllexport) Database *my_dllfunc()
{
    Database<T, T2> *db = NULL;
    if( <cond1> )
    {
        db = new SQLiteDatabase<const char *, wstring>();
    }
    if( <cond2> )
    {
        db = new ODBCDatabase<SQLWCHAR *, SQLWCHAR*>();
    }
    db->Connect();
}

并且在主要应用中:

class CMainFrame
{
public:
    CMainFrame();
    ~CmainFrame();
    void Foo();
private:
    Database *m_pdb;
}
CMainFrame::CMainFrame()
{
    m_pdb = NULL;
}
CMainFrame::~CMainFrame()
{
    delete m_pdb;
    m_pdbc = NULL;
}
void CMainFrame::Foo()
{
    HMODULE instance = ::LoadLibrary( "my_dll" );
    MYFUNC func = (MYFUNC)::GetProcInstance( "my_dllfunc", instance );
    m_pdb = func();
}

然而,看起来这个设计不会编译,因为模板变量不存在。

  1. 这是我想避免的:

在数据库.h:中

class __declspec(dllexport) Database
{
protected:
    struct Impl;
    Impl *pimpl;
    virtual int GetTableListFromDb(std::vector<std::wstring> &errorMsg);
    virtual int GetTableListFromDb(std::vector<SQLWCHAR *> &errorMsg);
public:
    virtual ~Database() = 0;
    Impl &GetTableVector() { return *pimpl; };
    virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
    virtual int Connect(SQLWCHAR *selectedDSN, std::vector<SQLWCHAR *> &errorMsg);
    virtual int Disconnect(std::vector<T2> &errorMsg) = 0;
};

在sqlite_db.h:中

class __declspec(dllexport) SQLiteDatabase : public Database
{
public:
    SQLiteDatabase();
    virtual ~SQLiteDatabase();
    virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
    virtual int Disconnect(std::vector<std::wstring> &errorMsg);
protected:
    void GetErrorMessage(int code, std::wstring &errorMsg);
    virtual int GetTableListFromDb(std::vector<std::wstring> &errorMsg);
private:
    sqlite3 *m_db;
};

在odbc_db:中

class __declspec(dllexport) ODBCDatabase : public Database
{
public:
    ODBCDatabase();
    virtual ~ODBCDatabase();
    virtual int Connect(SQLWCHAR *selectedDSN, std::vector<SQLWCHAR *> &errorMsg);
    virtual int Disconnect(std::vector<SQLWCHAR *> &errorMsg);
protected:
    int GetErrorMessage(std::vector<SQLWCHAR *> &errorMsg);
    virtual int GetTableListFromDb(std::vector<SQLWCHAR *> &errorMsg);
private:
    SQLHENV m_env;
    SQLHDBC m_hdbc;
    SQLHSTMT m_hstmt;
    SQLHWND m_handle;
};

用法-可能是这样的(?):

在某些dll中:

extern"C"__declspec(dllexport)数据库*my_dllfunc(){数据库*db;if()db=新SQLiteDatabase();if()db=新ODBCDatabase();}

主要应用:

class CMainFrame
{
public:
    CMainFrame();
    ~CmainFrame();
    void Foo();
private:
    Database *m_pdb;
}
CMainFrame::CMainFrame()
{
    m_pdb = NULL;
}
CMainFrame::~CMainFrame()
{
    delete m_pdb;
    m_pdbc = NULL;
}
void CMainFrame::Foo()
{
    HMODULE instance = ::LoadLibrary( "my_dll" );
    MYFUNC func = (MYFUNC)::GetProcInstance( "my_dllfunc", instance );
    m_pdb = func();
}

除了"Database"类对于每一个所需的函数都变得不必要地膨胀,然后在m_pdb销毁时出现崩溃,我可以复制它,只是不需要简单的代码。

我使用wxWidgets作为GUI,它在那里是可复制的。

希望现在你能看到我在找什么。

它看起来像我放在1`里的。即使在C++11中也是不可能的。所以,现在的问题变成了——什么样的API可以用来实现这一点?

基本上,我正在寻找一组类来处理使用不同API的不同数据库。

作为一个例子,我使用了SQLite和ODBC。

TIA!

[编辑]

API#2无法工作,因为:

  1. class数据库在.dll/.a库中(即静态链接)
  2. 类SQLiteDatabase和ODBCDatabase在其自己的.dll/.so/.dlib(即动态链接)中。它们链接到database_dll
  3. 将database_dll链接到主应用程序没有多大意义。因此,Database类变成了具有一堆纯虚拟函数的Database类接口。这意味着,现在尝试实现SQLiteDatabase,我将需要在odbc32.lib中进行链接,这是完全不必要的
  4. 我正在使用wxWidgets来实现GUI。这是C++库,不像MFC那样使用malloc()/free()进行内存管理。因此,试图在DLL中分配内存,并在主应用程序中释放内存将崩溃。我有一个完整的MFC和wx代码-第一个有效,第二个无效。我使用的是MSVC 2010 MFC版本和最新稳定的wx版本-3.1

因此,总而言之,从第2点开始的API将不起作用。

[/EDIT]

[编辑2]

静态DLL:

class Database
{
public:
    Database();
    ~Database();
    int Connect();
    int Disconnect();
};

动态DLL1:

class SQLiteDatabase : public Database
{
public:
    SQLiteDatabase();
    ~SQLiteDatabase();
    int Connect();
    int Disconnect();
};

动态DLL2:

class ODBCDatabase : public Database
{
public:
    ODBCDatabase();
    ~ODBCDatabase();
    int Connect();
    int Disconnect();
};

这就是我想要的界面。静态库数据库类代码应该只在头文件中,这样就不会显式链接到主应用程序。如何处理SQLite和ODBC操作的不同数据类型?

事实上,我正在寻找比这更好的东西:

class __declspec(dllexport) Database
{
public:
    Database();
    ~Database();
    int Connect(const char *dbName, std::vector<std::wstring> &errorMsg) = 0;
    int Connect(SQLWCHAR *dbName, std::vector<SQLWCHAR *> &errorMsg) = 0;
// possibly more Connect() overwrites
    int Disconnect() = 0;
};

这个接口很难看,因为所有那些Connect()函数都需要维护。

此外,现在SQLite接口将不得不引入odbc库,这是它不应该依赖的,因为第二个纯虚拟Connect()必须在任何地方实现。

那么,还有更好的东西吗?

[/EDIT]

[编辑3]

我实现了xvan的建议,现在一切都可以编译了——至少在带有MSVC 2010的Windows上是这样。不幸的是,运行程序时会崩溃。

它的编写方式是在DLL中分配内存,然后将指针传递回主应用程序。然后主应用程序应该管理指针并将其发送到另一个dll或使用它本身。当应用程序结束时,应该调用Disconnect()函数并销毁指针(通过析构函数释放内存)。

现在这种方法的问题是,一旦我把指针放在主应用程序中,我就不再有关于这个指针是什么的信息。这意味着,在MSVC调试器中,如果我打开主应用程序内的指针,我就看不到派生类的行,因此删除对象会崩溃。

我想也许我可以把指针推回到分配它的dll,但它也崩溃了,因为对象失去了层次结构,无法保留它。

那么,有没有什么方法可以让它在不分配主应用程序内存的情况下工作呢?

[/EDIT3]

您需要定义一个通用接口,然后实现从该接口到该接口的适当转换功能:

以下代码未经测试。

class Database
{
public:
    virtual ~Database()=0;
    virtual int Connect(std::wstring,std::vector<std::wstring> &) =0;
    virtual int Disconnect()=0;
};

class SQLiteDatabase : public Database
{
public:
    SQLiteDatabase();
    ~SQLiteDatabase();
    int Connect(std::wstring,std::vector<std::wstring> &);
    int Disconnect();
};

int SQLiteDatabase::Connect(std::wstring dbName,std::vector<std::wstring> &errMsg)
{
   std::string sqlite_dbName(dbName.begin(), dbName.end());
   std::vector<std::wstring> &  sqlite_errMsg = errMsg;
   /* Call real connect*/
   real_connect( sqlite_dbName.c_str(), sqlite_errMsg);
   /*              */
   return errMsg.size();
}

class ODBCDatabase : public Database
{
    int SQLiteDatabase::Connect(std::wstring dbName,std::v
public:
    ODBCDatabase();
    ~ODBCDatabase();
    int Connect(std::wstring,std::vector<std::wstring> &);
    int Disconnect();
};

int ODBCDatabase::Connect(std::wstring dbName,std::vector<std::wstring> &errMsg){
    SQLWCHAR * odbc_dbName = dbName.c_str();
    std::vector<SQLWCHAR *> & odbc_errMsg;
    /* Call real connect*/
    real_connect(odbc_dbName, odbc_errMsg);
    /*Not sure if this'll work, but if it doesn't, manually copy strings from odbc_errMsg to errMsg*/
    errMsg = std::move( std::vector<std::wstring>(odbc_errMsg.begin(), odbc_errMsg.end()));
    return  errMsg.size();
    }