65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (6投票s)

2013年3月28日

CPOL

4分钟阅读

viewsIcon

36178

downloadIcon

15

描述了一个适用于 Windows 的 LevelDB 版本, 该版本可以使用相同的 DLL 在 g++ 和 Visual C++ 中工作

介绍 

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)); 

代码创建了打开数据库的选项。我们将其设置为如果数据库不存在则创建。我们还设置了LRUCacheBloomFilterPolicy

// 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日。
© . All rights reserved.