boost multi_index_container 多索引容器的使用

多索引容器 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;

}

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章