相信想必大家在实际开发中一定多少会遇到一些的问题,我需要创建一个可以索引的容器,例如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
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:
};
bool ticketPool::ticketPush(Ticket& ticket)
{
this->ticket_pool.insert(ticket);
return true;
}
当然插入之后,表会自动按排序规则更新,完全不用担心
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(由ordered_index索引器提供)
可以自己去看boost文档
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;
}
因篇幅问题不能全部显示,请点此查看更多更全内容