多索引容器 multi_index_container
爲什麼要使用multi_index_container
相信想必大家在實際開發中一定多少會遇到一些的問題,我需要創建一個可以索引的容器,例如map,set 但是無論map還是set 都是單索引的,例如建立一個類,類裏有多個成員變量我們都要對應的索引便於查找 例如:
class Ticket
{
public:
int ticketHeight;//塊高
uint256 ticketTxHash;//票hash
CAmount ticketPrice;//當前票的票價
CBitcoinAddress ticketAddress;//買票的地址
Ticket();
Ticket(const Ticket& ticket);
Ticket(int ticketHeight, uint256 ticketTxHash, CAmount ticketPrice, CBitcoinAddress ticketAddress);
void operator=(Ticket & ticket)
{
this->ticketAddress = ticket.ticketAddress;
this->ticketHeight = ticket.ticketHeight;
this->ticketPrice = ticket.ticketPrice;
this->ticketTxHash = ticket.ticketTxHash;
}
bool operator==(Ticket ticket)
{
if (this->ticketAddress == ticket.ticketAddress &&
this->ticketHeight == ticket.ticketHeight &&
this->ticketTxHash == ticket.ticketTxHash &&
this->ticketPrice == ticket.ticketPrice)
{
return true;
}
return false;
}
std::string toString();
~Ticket();
private:
};
我需要對ticketHeight,ticketTxHash 經行排序索引,如果只是使用map,set構建索引的話,只能對應一種索引,ticketTxHash 或者ticketHeight,而我們需要使用兩個map或者set纔可以完成對應的需求,那麼有沒有一種結構可以對應兩個排序索引——答案是有的 就是我們要說的 boost庫中的 multi_index_container
下面就來說一下 multi_index_container 的基本使用,以及我遇到的坑,希望大家不要踩到;
創建
multi_index_container可以看作加載在內存中的數據庫,不多說直接上代碼
struct ticket_tx_hash {};//爲排序索引後表建立的表明()
struct ticket_height {};
typedef
boost::multi_index_container<
Ticket, //此處爲你要排序的成員類名
indexed_by< //indexed_by<...> 中填寫索引規則
ordered_unique< //ordered_unique 類比數據庫中的主鍵爲唯一索引,ordered默認升序,還有其他順序下面會做具體描述
//tag<...> 指定表名 需要聲明即上面的結構體名
//BOOST_MULTI_INDEX_MEMBER boost的宏,表示在目標中選取成員作爲排序依據
//(Ticket, uint256, ticketTxHash) Ticket:容器成員 uint256:排序依據的類型 ticketTxHash :排序依據
tag<ticket_tx_hash>, BOOST_MULTI_INDEX_MEMBER(Ticket, uint256, ticketTxHash)>,
ordered_non_unique<//非唯一索引——可與存在多個
tag<ticket_height>, BOOST_MULTI_INDEX_MEMBER(Ticket, int, ticketHeight)>
>
> ticketpool_set;
ticketpool_set ticket_pool;
這樣就完成了一個多索引容器類型ticketpool_set的建立,接着實例化了一個ticketpool_set類型的ticket_pool
補充1 :排序不止ordered_unique/ordered_non_unique一種,還有hashed_unique,下面是一段比特幣mempool代碼,以及指定排序規則
typedef boost::multi_index_container<
CTxMemPoolEntry,
boost::multi_index::indexed_by<
// hashed_unique 按照hash排序
boost::multi_index::hashed_unique<mempoolentry_txid, SaltedTxidHasher>,
//指定排序規則 CompareTxMemPoolEntryByDescendantScore
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<descendant_score>,
boost::multi_index::identity<CTxMemPoolEntry>,
CompareTxMemPoolEntryByDescendantScore
>,
。。。。//省略
不同索引的表 以及 迭代器
兩種取迭代器的方法(固定格式)
//index<ticket_tx_hash>指定tag 爲 ticket_tx_hash的表的迭代器
typedef ticketpool_set::index<ticket_tx_hash>::type::iterator Ticketiter;
//index<0> 去上述排序規則中的第一條規則,實際效果和index<ticket_tx_hash>一樣,
// 規則序號自0 開始
//typedef ticketpool_set::index<0>::type::iterator Ticketiter;
取表
//取出規則需要爲0的表,
ticketpool_set::index<ticket_tx_hash>::type & ticket_tx_hash_pool = this->ticket_pool.get<0>();
//取出規則需要爲ticket_tx_hash的表,
ticketpool_set::index<ticket_tx_hash>::type & ticket_tx_hash_pool = this->ticket_pool.get<ticket_tx_hash>();
補充2: 在迭代的時候迭代器取出來的爲const 類型的,需要注意,通過const_cast可以去除const
使用
ticketPool類完整版
class ticketPool
{
public:
ticketPool();
~ticketPool();
struct ticket_tx_hash {};
struct ticket_height {};
typedef
boost::multi_index_container<
Ticket,
indexed_by<
ordered_unique<
tag<ticket_tx_hash>, BOOST_MULTI_INDEX_MEMBER(Ticket, uint256, ticketTxHash)>,
ordered_non_unique<
tag<ticket_height>, BOOST_MULTI_INDEX_MEMBER(Ticket, int, ticketHeight)>
>
> ticketpool_set;
ticketpool_set ticket_pool;
typedef ticketpool_set::index<ticket_tx_hash>::type::iterator Ticketiter;
//typedef ticketpool_set::index<0>::type::iterator Ticketiter;
bool ticketPush(Ticket& ticket);
bool ticketDel(Ticket ticket);
bool ticketDelByHeight(int height);
void print_ticket()
{
for (Ticketiter ticketiter = this->ticket_pool.get<ticket_tx_hash>().begin(); ticketiter != this->ticket_pool.get<ticket_tx_hash>().end(); ticketiter++)
{
Ticket ticket = *ticketiter;
std::cout << ticket.toString() << std::endl;
}
std::cout << "------------------------------------------------------------------------" << std::endl;
for (ticketpool_set::index<ticket_height>::type::iterator ticketiter = this->ticket_pool.get<ticket_height>().begin(); ticketiter != this->ticket_pool.get<ticket_height>().end(); ticketiter++)
{
Ticket ticket = *ticketiter;
std::cout << ticket.toString() << std::endl;
}
}
//
Ticket ticketAt(unsigned int index);
private:
};
增加(insert)
bool ticketPool::ticketPush(Ticket& ticket)
{
this->ticket_pool.insert(ticket);
return true;
}
當然插入之後,表會自動按排序規則更新,完全不用擔心
刪除 (erase)
ticket_tx_hash_pool.erase(ticketiter);
補充3:如果拿原有表直接刪除是不行的,因爲原表的爲const,這裏我是取出表之後在經行刪除的。完整代碼如下
bool ticketPool::ticketDel(Ticket ticket)
{
ticketpool_set::index<ticket_tx_hash>::type & ticket_tx_hash_pool = this->ticket_pool.get<ticket_tx_hash>();
for (Ticketiter ticketiter = this->ticket_pool.get<ticket_tx_hash>().begin(); ticketiter != this->ticket_pool.get<ticket_tx_hash>().end(); ticketiter++)
{
//上面說了迭代器取出的是有const屬性的,所以(*ticketiter)==ticket,會導致boost編譯時出錯,也有一部分原因是我重載== 號的時候時沒有const屬性的原因
if (ticket == (*ticketiter))
{
ticket_tx_hash_pool.erase(ticketiter);
return true;
}
}
return false;
}
補充4 : 刪除當然也可以刪除一段內容(需要排序後的表),內容爲兩個迭代器之間的所有東西
bool ticketPool::ticketDelByHeight(int height)
{
ticketpool_set::index<ticket_height>::type::iterator pend;
ticketpool_set::index<ticket_height>::type::iterator pbegin = this->ticket_pool.get<1>().begin();
while (pbegin != this->ticket_pool.get<ticket_height>().end())
{
if (pbegin->ticketHeight == height)
{
break;
}
pbegin++;
}
pend = pbegin;
while (pend->ticketHeight ==height && pend!= this->ticket_pool.get<ticket_height>().end())
{
pend++;
}
this->ticket_pool.get<ticket_height>().erase(pbegin, pend);
return true;
}
修改(replace和modify)
因爲我時在寫區塊鏈底層代碼,所以我的代碼裏沒有修改這個方法,所以就簡單介紹一下修改方法。
修改分爲兩種方法replace和modify(由ordered_index索引器提供)
可以自己去看boost文檔boost中文文檔鏈接
查看(find)
CBitcoinAddress a1("qfWkAzh1DqJTtNegiY7sEQteJUPNxKHEER");
icketpool_set::index<ticket_height>::type::iterator it = this->ticket_pool.get<1>().find(a1);
最後附上完整的代碼及測試代碼和測試結果,需要結合比特幣代碼看下,如果只是學習使用就夠了
// ticketpool.h
#pragma once
#include "uint256.h"
#include "base58.h"
#include "amount.h"
#include <list>
#include <string>
#include "boost/multi_index/identity.hpp"
#include "boost/multi_index/member.hpp"
#include "boost/multi_index_container.hpp"
#include "boost/multi_index/ordered_index.hpp"
#include "boost/multi_index/hashed_index.hpp"
#include <boost/multi_index/sequenced_index.hpp>
using boost::multi_index_container;
using namespace boost::multi_index;
class Ticket
{
public:
int ticketHeight;//塊高
uint256 ticketTxHash;//票hash
CAmount ticketPrice;//當前票的票價
CBitcoinAddress ticketAddress;//買票的地址
Ticket();
Ticket(const Ticket& ticket);
Ticket(int ticketHeight, uint256 ticketTxHash, CAmount ticketPrice, CBitcoinAddress ticketAddress);
void operator=(Ticket & ticket)
{
this->ticketAddress = ticket.ticketAddress;
this->ticketHeight = ticket.ticketHeight;
this->ticketPrice = ticket.ticketPrice;
this->ticketTxHash = ticket.ticketTxHash;
}
bool operator==(Ticket ticket)
{
if (this->ticketAddress == ticket.ticketAddress &&
this->ticketHeight == ticket.ticketHeight &&
this->ticketTxHash == ticket.ticketTxHash &&
this->ticketPrice == ticket.ticketPrice)
{
return true;
}
return false;
}
std::string toString();
~Ticket();
private:
};
class ticketPool
{
public:
ticketPool();
~ticketPool();
struct ticket_tx_hash {};
struct ticket_height {};
typedef
boost::multi_index_container<
Ticket,
indexed_by<
ordered_unique<
tag<ticket_tx_hash>, BOOST_MULTI_INDEX_MEMBER(Ticket, uint256, ticketTxHash)>,
ordered_non_unique<
tag<ticket_height>, BOOST_MULTI_INDEX_MEMBER(Ticket, int, ticketHeight)>
>
> ticketpool_set;
ticketpool_set ticket_pool;
typedef ticketpool_set::index<ticket_tx_hash>::type::iterator Ticketiter;
//typedef ticketpool_set::index<0>::type::iterator Ticketiter;
bool ticketPush(Ticket& ticket);
bool ticketDel(Ticket ticket);
bool ticketDelByHeight(int height);
void print_ticket()
{
for (Ticketiter ticketiter = this->ticket_pool.get<ticket_tx_hash>().begin(); ticketiter != this->ticket_pool.get<ticket_tx_hash>().end(); ticketiter++)
{
Ticket ticket = *ticketiter;
std::cout << ticket.toString() << std::endl;
}
std::cout << "------------------------------------------------------------------------" << std::endl;
for (ticketpool_set::index<ticket_height>::type::iterator ticketiter = this->ticket_pool.get<ticket_height>().begin(); ticketiter != this->ticket_pool.get<ticket_height>().end(); ticketiter++)
{
Ticket ticket = *ticketiter;
std::cout << ticket.toString() << std::endl;
}
}
//
Ticket ticketAt(unsigned int index);
private:
};
//ticketpool.cpp
#include "ticketpool.h"
#include "tinyformat.h"
Ticket::Ticket()
{
}
Ticket::Ticket(const Ticket& ticket)
{
this->ticketAddress = ticket.ticketAddress;
this->ticketHeight = ticket.ticketHeight;
this->ticketPrice = ticket.ticketPrice;
this->ticketTxHash = ticket.ticketTxHash;
}
Ticket::Ticket(int ticketHeight, uint256 ticketTxHash, CAmount ticketPrice, CBitcoinAddress ticketAddress)
{
this->ticketAddress =ticketAddress;
this->ticketHeight = ticketHeight;
this->ticketPrice = ticketPrice;
this->ticketTxHash = ticketTxHash;
}
Ticket::~Ticket()
{
}
std::string Ticket::toString()
{
std::string ticketStr;
ticketStr += strprintf("ticket info:\t ticketAddress = %s \t ticketHeight = %d \t ticketPrice = %d \t ticketTxHash = %s \n",
this->ticketAddress.ToString(),
this->ticketHeight,
this->ticketPrice,
this->ticketTxHash.ToString());
return ticketStr;
}
ticketPool::ticketPool()
{
}
ticketPool::~ticketPool()
{
}
bool ticketPool::ticketPush(Ticket& ticket)
{
this->ticket_pool.insert(ticket);
return true;
}
bool ticketPool::ticketDel(Ticket ticket)
{
ticketpool_set::index<ticket_tx_hash>::type & ticket_tx_hash_pool = this->ticket_pool.get<ticket_tx_hash>();
for (Ticketiter ticketiter = this->ticket_pool.get<ticket_tx_hash>().begin(); ticketiter != this->ticket_pool.get<ticket_tx_hash>().end(); ticketiter++)
{
if (ticket == (*ticketiter))
{
ticket_tx_hash_pool.erase(ticketiter);
return true;
}
}
return false;
}
bool ticketPool::ticketDelByHeight(int height)
{
ticketpool_set::index<ticket_height>::type::iterator pend;
ticketpool_set::index<ticket_height>::type::iterator pbegin = this->ticket_pool.get<1>().begin();
t
while (pbegin != this->ticket_pool.get<ticket_height>().end())
{
if (pbegin->ticketHeight == height)
{
break;
}
pbegin++;
}
pend = pbegin;
while (pend->ticketHeight ==height && pend!= this->ticket_pool.get<ticket_height>().end())
{
pend++;
}
this->ticket_pool.get<ticket_height>().erase(pbegin, pend);
return true;
}
Ticket ticketPool::ticketAt(unsigned int index)
{
Ticketiter ticketiter = this->ticket_pool.get<ticket_tx_hash>().begin();
if (index >= this->ticket_pool.size() )
{
throw std::runtime_error("ticket index error");
}
for (unsigned int i = 0 ; i < index ; i++)
{
ticketiter++;
}
return *ticketiter;
}
#include <stdio.h>
#include <iostream>
#include <primitives/block.h>
#include "ticketpool.h"
#include "base58.h"
int main(int argc, char* argv[])
{
printf("hello world!\n");
CBitcoinAddress a1("qfWkAzh1DqJTtNegiY7sEQteJUPNxKHEER");
CBitcoinAddress a2("qKyMFKqhTAUu6ka9t2nZ1bxYz1bAYfmuVN");
CBitcoinAddress a3("qL3VbDUTbtm8MN9ii8Xm5DZe25LvYhxoe1");
CBitcoinAddress a4("qMZupjWUQKFJpY63SvPrqz7R791CYUCsFn");
CBitcoinAddress a5("qTLkcQALhxsqetNvkwdQ87XeVaeYoWcHx7");
CBitcoinAddress a6("qRpj8gKWNHcYN3HjPpi5p8912fntEX6x5p");
uint256 u1;
u1.SetHex("0x1c2f3fb3ada4973384f3b1e84267fe7e80888a121b14480b74ed708329370deb");
uint256 u2;
u2.SetHex("0x2c2f3fb3ada4973384f3b1e84267fe7e80888a121b14480b74ed708329370deb");
uint256 u3;
u3.SetHex("0x7c2f3fb3ada4973384f3b1e84267fe7e80888a121b14480b74ed708329370deb");
uint256 u4;
u4.SetHex("0x4c2f3fb3ada4973384f3b1e84267fe7e80888a121b14480b74ed708329370deb");
uint256 u5;
u5.SetHex("0x5c2f3fb3ada4973384f3b1e84267fe7e80888a121b14480b74ed708329370deb");
uint256 u6;
u6.SetHex("0x6c2f3fb3ada4973384f3b1e84267fe7e80888a121b14480b74ed708329370deb");
std::cout << " a " << std::endl;
Ticket t1(1,u1,1000, a1);
std::cout << " a " << std::endl;
Ticket t2(1, u2, 1000, a2);
Ticket t3(2, u3, 1000, a3);
Ticket t4(2, u4, 1000, a4);
Ticket t5(3, u5, 1000, a5);
Ticket t6(2, u6, 1000, a6);
std::cout <<" " <<t1.toString() << std::endl;
ticketPool pool;
pool.ticketPush(t1);
pool.ticketPush(t2);
pool.ticketPush(t3);
pool.ticketPush(t4);
pool.ticketPush(t5);
pool.ticketPush(t6);
pool.print_ticket();
std::cout << " \n " << pool.ticketAt(2).toString() << std::endl;
std::cout << " \n " << pool.ticketAt(1).toString() << std::endl;
std::cout << " \n " << pool.ticketAt(0).toString() << std::endl;
//pool.ticketDel(t4);
pool.ticketDelByHeight(2);
std::cout << " -------------delete --------------------- " << std::endl;
pool.print_ticket();
return 0;
}