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;

}

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值