티스토리 뷰

뭔문 및 참조 : https://github.com/jbandela/leveldb_cross_compiler

http://www.codeproject.com/Articles/569146/LevelDB-DLL-for-Windows-A-New-Approach-to-Exportin


LevelDB는 구글에서 개발한 key/value 저장소이다. 이것을 윈도우에서 빌드하는 것은 힘든 일이다. 

이글을 쓰는 시점에서 http://code.google.com/p/leveldb/source/browse/WINDOWS?name=windows 는 가용한 글이 아님을 미리 밝혀둔다.

이 쓰레드에서 https://groups.google.com/forum/#!topic/leveldb/VuECZMnsob4 방안을 제시하지만, 역시 적절하지 않다.

Exporting a C++ class from a DLL can be hard if you want it be to be able to be used by different compilers. Alex Bleckhman as an excellent article here on Code Project titled  HowTo: Export C++ classes from a DLL. However, doing that can still be a pain as you cannot use exceptions, C++ types such as std::string. In addition, if you want to make a COM interface so you can have memory management and interface management, you still have a lot of code to write. 

This article uses the free library at https://github.com/jbandela/cross_compiler_call to build a wrapper library for leveldb. The full code can be found here

I packaged the needed files in the attached zip file. You can also get the file from here 

Note, while there is a C wrapper for leveldb that I could have used, I decided to do it this way to try out the above library in developing something with real world use. 

In this article, I will be talking about how the use the package. This will not be a tutorial on using cross_compiler_call to build something like this. If there is enough interest in the comments, I will write another article providing a walk-through of how this package was built. 

배경

먼저, 나는 LevelDB 라이브러리를 만들어야 한다.  윈도우 상에서 빌드하기 위한 LevelDB 버전을 찾는 것은 어려운 일이었다. 

첫번째 시도 :  https://code.google.com/p/leveldbwin/

두번째 시도 : https://code.google.com/r/kkowalczyk-leveldb/

그러나, 이 글들은 leveldb의 오래된 버전에 대한 것이었다.  이리 저리 찾다가,....

세번째 시도:

GitHub에서 bitcoin을 발견했다. 나는 이것이 잘 관리되고 있다는 것을 직감했다.

소스내에서, 그들은 그들은 윈도우를 지원하는 leveldb를 관리하고 있었다. 그러나, 여기에도 역시 Visual C++ Project파일은 존재하지 않았다. 빌드하기 위해서 나는 MinGW g++을 사용했는데, 이것은 nuwen.net 에서 다운받아 설치했고, msys 를 ".a" 파일을 생성하는 shell로 사용하였다. 즉, leveldb 라이브러리를 빌드한 것이다.

그리고 나서, 나는 방금 생성한 ".a" 스타일의 라이브러리를 윈도우의 DLL로 만들기 위해서,  leveldb_cc_dll.cpp 를 g++로 컴파일했다.

코드 사용하기

이 코드를 사용하는 예제를 위해, example.cpp를 보기 바란다.

당신은 variadic templates를 지원하는 C++ 11 버전이 필요하다. 

당신이 만일 g++을 사용한다면, 당신은 명령행에 -std=c++11을 기입해야 하며, 이를 어길시에는 수많은 에러에 직면하게 될 것이다.(:-))

Visual C++에서 빌드하기 위해서, 당신은 'November CTP of Visual Studio 2012'가 필요할 것이며, 이것은 여기에서 다운받을 수 있다. 다음은 마이크로소프트의 릴리즈 노트 중에서 디테일 부분을 발췌한 것이다.

    • "Visual C++ Compiler November 2012 CTP contains a preview release of the Visual C++ compiler that adds the the following C++11 features to the list of features already supported in Visual Studio 2012: uniform initialization, initializer lists, variadic templates, function template default arguments, delegating constructors, explicit conversion operators and raw strings. A tour of these features is available on Channel 9 Core C++ episode 6: http://aka.ms/vc-ctp-tour. More details on this release are available on the Visual C++ Blog: http://aka.ms/vc-ctp-blog "

일단 이러한 것을 전부 얻었다면, 이제 당신은 example.cpp를 컴파일할 수 있다. 유의할 것은 level_db_cc.dll이 실행파일이 실행할 동일한 디렉터리에 있어야 한다는 것이다.

이제 example.cpp를 설명하도록 하겠다.

#include <iostream>
#include  "leveldb_cc/level_db_interfaces.h" 

적색 표기된 줄에서 인터페이스 헤더를 포함하고 있다.

using namespace leveldb_cc; 

leveldb interfaces는 leveldb_cc 네임스페이스 내에 있다. 게다가, MSVC컴파일러는 버그를 가지고 있는데, 네임 룰업에 영향을 준다.  만일, 이를 포함하지 않으면, 당신은 Visual C++에서 컴파일러 에러에 직면하게 될 것이다.

int main(){
    cross_compiler_interface::module m("leveldb_cc_dll"); 

이것은 명세된 DLL을 로드할 모듈을 생성한다. 여기서 주의할 점은 .dll확장자를 제거한 파일명을 기입해야 한다는 것이다.  윈도우 환경에서 .dll이고, 리눅스에서는 .so이다. 모듈은 스콥을 벗어나게 되면 라이브러리를 자동으로 언로드한다.:

auto creator = cross_compiler_interface::create_unknown(m,"CreateLevelDBStaticFunctions")
        .QueryInterface<leveldb_cc::ILevelDBStaticFunctions>();

클래스 팩토리 인터페이스를 생성하기 위해서, DLL에 있는 함수  CreateLevelDBStaticFunctions 을 호출한다. create_unknown 은 IUknown을 리턴한다. 그래서, 우리는  ILevelDBStaticFunctions를 얻기 위해서 QueryInterface 를 호출한다.

// 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가 스콥을 벗어나면('}') 우리는 그 데이터베이스를 제거한다.

'NoSQL' 카테고리의 다른 글

mongoose - An Embedded Web Server  (0) 2013.07.09
leveldb - 윈도우 포팅 브랜치 버전  (0) 2013.07.05
NoSQL Review  (0) 2013.03.06