Windows 版 LevelDB DLL - 导出 C++ 类的新方法






4.82/5 (6投票s)
描述了一个适用于 Windows 的 LevelDB 版本,
介绍
LevedDB 是Google开发的一个键值存储。 在Windows上编译它可能会很痛苦。
从DLL导出C++类可能很困难,如果您希望它能被不同的编译器使用。 Alex Bleckhman 在Code Project上有一篇非常出色的文章,标题是 如何:从DLL导出C++类。 然而,这样做仍然可能很麻烦,因为您不能使用异常、C++类型,如std::string
。此外,如果您想创建一个COM接口以便进行内存管理和接口管理,您仍然需要编写大量代码。
本文使用 https://github.com/jbandela/cross_compiler_call 处的免费库来构建leveldb的包装器库。完整的代码可以在这里找到。
我将所需文件打包在附件的zip文件中。您也可以在此处获取文件。
注意,虽然有一个C语言版本的leveldb包装器我本可以使用,但我决定采用这种方式来尝试上述库,并开发一个具有实际用途的东西。
在本文中,我将讨论如何使用该包。本文不是关于如何使用cross_compiler_call来构建此类内容的教程。如果评论中有足够多的兴趣,我将撰写另一篇文章,详细介绍这个包是如何构建的。
背景
首先,我不得不构建leveldb库。在Windows上找到一个版本的leveldb进行编译是一个痛苦的过程。我尝试了 https://code.google.com/p/leveldbwin/ 和 https://code.google.com/r/kkowalczyk-leveldb/。但是,它们是leveldb的旧版本。然后我在GitHub上找到了bitcoin的代码库。我认为它会得到很好的维护。在源代码中,他们有一个支持Windows的leveldb发行版。但是,没有Visual C++项目。为了编译它,我使用了来自nuwen.net的MinGW G++,并使用msys作为shell来构建.a文件。然后我将leveldb_cc_dll.cpp编译成一个DLL,并链接到之前生成的.a文件。
使用代码
要查看使用此代码的示例,请查看example.cpp。您需要支持C++11的变长模板。如果您使用g++,则需要在命令行中使用-std=c++11,否则会遇到很多错误。要使用Visual C++进行编译,您需要Visual Studio 2012的11月CTP版本。您可以在这里下载。
一旦您准备好所有这些,只需编译example.cpp。确保level_db_cc.dll与exe文件位于同一目录中,然后运行exe文件。无需链接任何内容。
我们将通过example.cpp的各个部分来展示这是如何完成的。
#include <iostream> #include "leveldb_cc/level_db_interfaces.h"
第二行包含了定义我们接口的文件。
using namespace leveldb_cc;
leveldb接口位于leveldb_cc
命名空间下。此外,MSVC编译器中存在一个影响名称查找的bug。如果您不包含此文件,Visual C++会产生编译器错误。
int main(){
cross_compiler_interface::module m("leveldb_cc_dll");
这会创建一个加载指定DLL的模块。请注意,您需要省略DLL扩展名。在Windows上,库会自动添加.dll,在Linux上,代码会添加.so。当模块超出范围时,它将自动卸载该库。
auto creator = cross_compiler_interface::create_unknown(m,"CreateLevelDBStaticFunctions")
.QueryInterface<leveldb_cc::ILevelDBStaticFunctions>();
调用DLL中的函数CreateLevelDBStaticFunctions
来创建类工厂接口。create_unknown
返回IUknown
。因此,我们调用QueryInterface
来获取ILevelDBStaticFunctions
。
// Open a scope so db goes out of scope so we can delete the database {
我们希望最终删除数据库,但如果数据库已打开,则无法删除。因此,我们打开一个作用域,以便db对象超出范围,从而关闭数据库。
auto options = creator.CreateOptions(); options.set_create_if_missing(true); options.set_write_buffer_size(8*1024*1024); // Set cache of 1MB options.set_block_cache(creator.NewLRUCache(1024*1024)); // Set bloom filter with 10 bits per key options.set_filter_policy(creator.NewBloomFilterPolicy(10));
代码创建了打开数据库的选项。我们将其设置为如果数据库不存在则创建。我们还设置了LRUCache
和BloomFilterPolicy
。
// Open the db auto db = creator.OpenDB(options,"c:/tmp/testdb"); auto wo = creator.CreateWriteOptions(); wo.set_sync(false); // Add a few key/value pairs in a batch auto wb = creator.CreateWriteBatch(); wb.Put("Key1","Value1"); wb.Put("Key2","Value2"); wb.Put("Key3","Value3"); wb.Put("Key4","Value4"); wo.set_sync(true); db.WriteBatch(wo,wb); auto ro = creator.CreateReadOptions(); // Save a snapshot auto snapshot = db.GetSnapshot(); // Add more stuff to db db.PutValue(wo,"AfterSnapshot1","More Value1"); // Use the snapshot ro.set_snapshot(snapshot); auto iter = db.NewIterator(ro); std::cout << "Iterator with snapshot\n"; for(iter.SeekToFirst();iter.Valid();iter.Next()){ std::cout << iter.key().ToString() << "=" << iter.value().ToString() << "\n"; }; std::cout << "\n\n"; // Clear the snapshot ro.set_snapshot(nullptr); db.ReleaseSnapshot(snapshot); auto iter2 = db.NewIterator(ro); std::cout << "Iterator without snapshot\n"; for(iter2.SeekToFirst();iter2.Valid();iter2.Next()){ std::cout << iter2.key().ToString() << "=" << iter2.value().ToString() << "\n"; }; std::cout << "\n\n"; db.DeleteValue(wo,"Key1"); auto iter3 = db.NewIterator(ro); std::cout << "Iterator after delete Key1 snapshot\n"; for(iter3.SeekToFirst();iter3.Valid();iter3.Next()){ std::cout << iter3.key().ToString() << "=" << iter3.value().ToString() << "\n"; };
代码设置了一个WriteBatch
,并将一些键值对作为一个批次写入。然后,代码保存了一个快照。然后,它添加另一个键,并使用和不使用快照进行迭代。代码还删除了一个值,并进行迭代以显示其已被删除。
}
// Delete the db
auto s = creator.DestroyDB("c:/tmp/testdb",creator.CreateOptions());
在db在闭合花括号处超出范围后,我们然后销毁数据库。
笔记和有趣的点
注意:此代码未经全面测试。请自行承担使用风险。 如果您发现任何错误,请告诉我,我会尝试修复它们。更好的是,fork此存储库并改进它。
我认为这段代码是编写一个可以在不同编译器(甚至像MSVC和G++这样差异很大的编译器)之间工作的接口的一个很好的练习。cross_compiler_call库使得这一点更容易,因为它支持在DLL边界之间传递字符串和向量。如果有兴趣,我很乐意讨论DLL是如何创建的。
请告诉我您的想法。
历史
- 初始版本:2013年3月28日。