对于我想要的东西,有更好的API吗
Is there a better API for the thing I want
考虑以下两个片段:
- 这就是我想要的:
数据库.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();
}
然而,看起来这个设计不会编译,因为模板变量不存在。
- 这是我想避免的:
在数据库.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无法工作,因为:
- class数据库在.dll/.a库中(即静态链接)
- 类SQLiteDatabase和ODBCDatabase在其自己的.dll/.so/.dlib(即动态链接)中。它们链接到database_dll
- 将database_dll链接到主应用程序没有多大意义。因此,Database类变成了具有一堆纯虚拟函数的Database类接口。这意味着,现在尝试实现SQLiteDatabase,我将需要在odbc32.lib中进行链接,这是完全不必要的
- 我正在使用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();
}
- C++:将控制台输出存储在宏中更好吗
- FFmpeg:制作一个应用程序比直接使用ffmepg更好吗
- 初始化具有非默认构造函数的std::数组项的更好方法
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 为什么新的随机库比std::rand()更好
- 寻找一种更好的方法来表示无符号字符数组
- 哪种方法更好,性能明智
- 什么更好?返回对象指针列表?或返回指向对象列表的指针?
- 什么是更好的做法?通过指针或标识符传递类成员?
- 寻求更好地理解标准::访问
- 线程消息传递或更好:在"大师班"中访问其他班级的成员
- 有没有更好的方法来处理异常? try-catch块真的很丑
- 如何更好地检查两个 char 变量是否在一组值中?
- 有没有更好的方法对C++中的三个整数进行排序?
- 什么模板用法在阶乘中更好
- 平面缓冲区可以利用向量中的 0 吗?还是其他小波比哈尔变换更好?
- 我们应该如何使用枚举类进行索引(或者我们应该更好地避免这种情况)?
- 需要更好地了解使用 Windows API 的智能指针
- 对于我想要的东西,有更好的API吗
- 为可变函数设计一个更好的API