C++ vector bounds checking - c++

I have a 6D vector and I need to check neighborhood of each element (2 elements in each direction). Of course when I am on boundary of the vector, check leads in the Segmentation fault. All I can do is having switch with ton of cases. Is there any better way to solve this? I also thought of something like try-catch.

Still too bulky but it works:
#include <iostream>
#include <array>
#include <vector>
typedef std::vector<int> Vector1D;
typedef std::vector<Vector1D> Vector2D;
typedef std::vector<Vector2D> Vector3D;
typedef std::vector<Vector3D> Vector4D;
typedef std::vector<Vector4D> Vector5D;
typedef std::vector<Vector5D> Vector6D;
typedef std::array<size_t, 6> Path;
bool GetVectorPathElement(Vector6D const &vector6D, Path const &path, int &val)
{
size_t i = 0, k = path[i];
if (vector6D.size() > k)
{
Vector5D const &vector5D = vector6D[k];
k = path[++i];
if (vector5D.size() > k)
{
Vector4D const &vector4D = vector5D[k];
k = path[++i];
if (vector4D.size() > k)
{
Vector3D const &vector3D = vector4D[k];
k = path[++i];
if (vector3D.size() > k)
{
Vector2D const &vector2D = vector3D[k];
k = path[++i];
if (vector2D.size() > k)
{
Vector1D const &vector1D = vector2D[k];
k = path[++i];
if (vector1D.size() > k)
{
val = vector1D[k];
return true;
}
}
}
}
}
}
std::cout << "Invalid path " << k << " at index " << i << std::endl;
return false;
}
int main()
{
Vector1D vector1D = { 1,2,3,4,5,6 };
Vector2D vector2D = { vector1D, vector1D, vector1D, vector1D, vector1D };
Vector3D vector3D = { vector2D, vector2D, vector2D, vector2D };
Vector4D vector4D = { vector3D, vector3D, vector3D };
Vector5D vector5D = { vector4D, vector4D };
Vector6D vector6D = { vector5D };
Path path = { 0,0,2,1,4,5 };
int element;
if (GetVectorPathElement(vector6D, path, element))
{
std::cout << "Path: ";
for (auto i : path)
std::cout << i << " ";
std::cout << "\nElement value at destination: " << element << std::endl;
}
return 0;
}
https://ideone.com/nL1zo2

Related

How can I optimise edge creation and vertex deletion in an Adjacency set representation of a graph?

I have a graph represented with an Adjacency set similar to:
struct Vertex {
int x;
bool operator==(const Vertex& b) {
return x==b.x;
}
};
template<> struct std::hash<Vertex> {
std::size_t operator()(Vertex const& v) const noexcept {
return std::hash<int>()(v.x);
}
};
struct Edge {
std::shared_ptr<Vertex> fr;
std::shared_ptr<Vertex> to;
double weight;
Edge(std::shared_ptr<Vertex> fr_in, std::shared_ptr<Vertex> to_in) : fr(fr_in), to(to_in) {};
};
class Graph{
public:
std::shared_ptr<Vertex> addVertex() {
auto new_vertex = std::make_shared<Vertex>();
mAdjacencySet[new_vertex] = {};
return new_vertex;
}
std::shared_ptr<Edge> addEdge(std::shared_ptr<Vertex> fr, std::shared_ptr<Vertex> to) {
auto edge = std::make_shared<Edge>(fr, to);
mAdjacencySet[fr][to] = edge;
return edge;
}
void deleteVertex(std::shared_ptr<Vertex> v) {
mAdjacencySet.erase(v);
for (auto& [key, val] : mAdjacencySet) {
val.erase(v);
}
};
private:
std::unordered_map<
std::shared_ptr<Vertex>,
std::unordered_map<
std::shared_ptr<Vertex>,
std::shared_ptr<Edge>,
Deref::Hash,
Deref::Compare
>,
Deref::Hash,
Deref::Compare
> mAdjacencySet;
};
After I build my graph, I need to prune as many edges as possible because they are expensive to calculate.
One of the strategies to do so, is to delete any vertices with out degree of zero, EXCEPT for the destination vertex. This is very slow to do, relative to the rest of my program.
I wrote a script to time the relative parts of the complexity of each part:
int main() {
Timer wholeProgram;
wholeProgram.start();
Graph g;
auto v1 = g.addVertex();
auto v2 = g.addVertex();
auto e = g.addEdge(v1, v2);
Timer makingVertices;
makingVertices.start();
size_t n = 1e3;
std::vector<std::shared_ptr<Vertex>> vertices(n);
for (size_t i=0; i<n; ++i) {
vertices[i] = g.addVertex();
vertices[i]->x = i;
}
makingVertices.stop();
Timer makingEdges;
makingEdges.start();
for (auto v1 : vertices) {
for (auto v2: vertices) {
if (v1!=v2) {
g.addEdge(v1, v2);
}
}
}
makingEdges.stop();
Timer deletingVertices;
deletingVertices.start();
for (auto vert : vertices) {
g.deleteVertex(vert);
}
deletingVertices.stop();
wholeProgram.stop();
std::cout << "Making Verts: " << makingVertices.elapsedMilliseconds() << std::endl;
std::cout << "Making edges: " << makingEdges.elapsedMilliseconds() << std::endl;
std::cout << "Deleting verts: " << deletingVertices.elapsedMilliseconds() << std::endl;
std::cout << "Whole program: " << wholeProgram.elapsedMilliseconds() << std::endl;
return 0;
}
And the timings (with '-O3') are:
Making Verts: 0
Making edges: 270
Deleting verts: 188
Whole program: 458
(In my actual code base, the deleting of the vertices is actually around 90% of the time to create the graph).
How can I optimize this code to reduce the time to delete vertices (And also I guess optimize the creation of edges, as this is also slow)?
The full code to run this example is:
#include <functional>
#include <memory>
#include <chrono>
#include <iostream>
class Timer
{
public:
void start()
{
m_StartTime = std::chrono::system_clock::now();
m_bRunning = true;
}
void stop()
{
m_EndTime = std::chrono::system_clock::now();
m_bRunning = false;
}
double elapsedMilliseconds()
{
std::chrono::time_point<std::chrono::system_clock> endTime;
if(m_bRunning)
{
endTime = std::chrono::system_clock::now();
}
else
{
endTime = m_EndTime;
}
return std::chrono::duration_cast<std::chrono::milliseconds>(endTime - m_StartTime).count();
}
double elapsedSeconds()
{
return elapsedMilliseconds() / 1000.0;
}
private:
std::chrono::time_point<std::chrono::system_clock> m_StartTime;
std::chrono::time_point<std::chrono::system_clock> m_EndTime;
bool m_bRunning = false;
};
struct Deref {
/**
* #brief Function to dereference the pointer when hashing elements in a hashmap of shared pointers
*
*/
struct Hash {
template <typename T> std::size_t operator()(std::shared_ptr<T> const& p) const
{
return std::hash<T>()(*p);
}
template <typename T> std::size_t operator()(T const & p) const
{
return std::hash<T>(p);
}
};
/**
* #brief Function to dereference the pointer when comparing elements in a hashmap of shared pointers
*
*/
struct Compare {
template <typename T> bool operator()(std::shared_ptr<T> const& a, std::shared_ptr<T> const& b) const
{
return *a == *b;
}
template <typename T> bool operator()(T const& a, T const& b) const
{
return a == b;
}
};
};
struct Vertex {
int x;
bool operator==(const Vertex& b) {
return x==b.x;
}
};
template<> struct std::hash<Vertex> {
std::size_t operator()(Vertex const& v) const noexcept {
return std::hash<int>()(v.x);
}
};
struct Edge {
std::shared_ptr<Vertex> fr;
std::shared_ptr<Vertex> to;
double weight;
Edge(std::shared_ptr<Vertex> fr_in, std::shared_ptr<Vertex> to_in) : fr(fr_in), to(to_in) {};
};
class Graph{
public:
std::shared_ptr<Vertex> addVertex() {
auto new_vertex = std::make_shared<Vertex>();
mAdjacencyList[new_vertex] = {};
return new_vertex;
}
std::shared_ptr<Edge> addEdge(std::shared_ptr<Vertex> fr, std::shared_ptr<Vertex> to) {
auto edge = std::make_shared<Edge>(fr, to);
mAdjacencyList[fr][to] = edge;
return edge;
}
void deleteVertex(std::shared_ptr<Vertex> v) {
mAdjacencyList.erase(v);
for (auto& [key, val] : mAdjacencyList) {
val.erase(v);
}
};
private:
std::unordered_map<
std::shared_ptr<Vertex>,
std::unordered_map<
std::shared_ptr<Vertex>,
std::shared_ptr<Edge>,
Deref::Hash,
Deref::Compare
>,
Deref::Hash,
Deref::Compare
> mAdjacencyList;
};
int main() {
Timer wholeProgram;
wholeProgram.start();
Graph g;
auto v1 = g.addVertex();
auto v2 = g.addVertex();
auto e = g.addEdge(v1, v2);
Timer makingVertices;
makingVertices.start();
size_t n = 1e3;
std::vector<std::shared_ptr<Vertex>> vertices(n);
for (size_t i=0; i<n; ++i) {
vertices[i] = g.addVertex();
vertices[i]->x = i;
}
makingVertices.stop();
Timer makingEdges;
makingEdges.start();
for (auto v1 : vertices) {
for (auto v2: vertices) {
if (v1!=v2) {
g.addEdge(v1, v2);
}
}
}
makingEdges.stop();
Timer deletingVertices;
deletingVertices.start();
for (auto vert : vertices) {
g.deleteVertex(vert);
}
deletingVertices.stop();
wholeProgram.stop();
std::cout << "Making Verts: " << makingVertices.elapsedMilliseconds() << std::endl;
std::cout << "Making edges: " << makingEdges.elapsedMilliseconds() << std::endl;
std::cout << "Deleting verts: " << deletingVertices.elapsedMilliseconds() << std::endl;
std::cout << "Whole program: " << wholeProgram.elapsedMilliseconds() << std::endl;
return 0;
}
And to run it, you can view it online

How to set Eigen DesnseFunctor input and value sizes for use in Eigen Levenberg Marquardt

Problem: I do not always know the exact size of the Jacobian or Function vector that I am going to use Levenberg Marquardt on. Therefore, I need to set the dimensions of them at compile time.
Expected: After I declare an instance of MyFunctorDense. I could set the "InputsAtCompileTime" to my input size and set "ValuesAtCompileTime" to my values size. Then my Jacobian ,aFjac, should have the dimensions tValues x tInputs, and my function vector, aH, should have the dimensions tValues x 1.
Observed:
.h file
#pragma once
#include "stdafx.h"
#include <iostream>
#include <unsupported/Eigen/LevenbergMarquardt>
#include <unsupported/Eigen/NumericalDiff>
//Generic functor
template <typename _Scalar, typename _Index>
struct MySparseFunctor
{
typedef _Scalar Scalar;
typedef _Index Index;
typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> InputType;
typedef Eigen::Matrix<Scalar,Eigen::Dynamic,1> ValueType;
typedef Eigen::SparseMatrix<Scalar, Eigen::ColMajor, Index>
JacobianType;
typedef Eigen::SparseQR<JacobianType, Eigen::COLAMDOrdering<int> >
QRSolver;
enum {
InputsAtCompileTime = Eigen::Dynamic,
ValuesAtCompileTime = Eigen::Dynamic
};
MySparseFunctor(int inputs, int values) : m_inputs(inputs),
m_values(values) {}
int inputs() const { return m_inputs; }
int values() const { return m_values; }
const int m_inputs, m_values;
};
template <typename _Scalar, int NX=Eigen::Dynamic, int NY=Eigen::Dynamic>
struct MyDenseFunctor
{
typedef _Scalar Scalar;
enum {
InputsAtCompileTime = NX,
ValuesAtCompileTime = NY
};
typedef Eigen::Matrix<Scalar,InputsAtCompileTime,1> InputType;
typedef Eigen::Matrix<Scalar,ValuesAtCompileTime,1> ValueType;
typedef Eigen::Matrix<Scalar,ValuesAtCompileTime,InputsAtCompileTime>
JacobianType;
typedef Eigen::ColPivHouseholderQR<JacobianType> QRSolver;
const int m_inputs, m_values;
MyDenseFunctor() : m_inputs(InputsAtCompileTime),
m_values(ValuesAtCompileTime) {}
MyDenseFunctor(int inputs, int values) : m_inputs(inputs),
m_values(values) {}
int inputs() const { return m_inputs; }
int values() const { return m_values; }
};
struct MyFunctorSparse : MySparseFunctor<double, int>
{
MyFunctorSparse(void) : MySparseFunctor<double, int>(2 , 2) {}
int operator()(const Eigen::VectorXd &aX, //Input
Eigen::VectorXd &aF) const; //Output
int df(const InputType &aF, JacobianType& aFjac);
};
struct MyFunctorDense : MyDenseFunctor<double>
{
MyFunctorDense(void) : MyDenseFunctor<double>( Eigen::Dynamic ,
Eigen::Dynamic) {}
int operator()(const InputType &aX, //Input
ValueType &aF) const; //Output
int df(const InputType &aX, JacobianType& aFjac);
};
.cpp file
#pragma once
#include "stdafx.h"
#include "Main.h"
int MyFunctorSparse::operator()(const Eigen::VectorXd &aX, //Input
Eigen::VectorXd &aF) const //Output
{
//F = aX0^2 + aX1^2
aF(0) = aX(0)*aX(0) + aX(1)*aX(1);
aF(1) = 0;
return 0;
}
int MyFunctorDense::operator()(const InputType &aX, //Input
ValueType &aF) const //Output
{
//F = aX0^2 + aX1^2
for (int i = 0; i < aF.size(); i++)
{
aF(i) = i*aX(0)*aX(0) + i*(aX(1)-1)*(aX(1)-1);
}
return 0;
}
int MyFunctorSparse::df(const InputType &aX, JacobianType& aFjac)
{
aFjac.coeffRef(0, 0) = 2*aX(0);
aFjac.coeffRef(0, 1) = 2*aX(1);
aFjac.coeffRef(1, 0) = 0.0;
aFjac.coeffRef(1, 1) = 0.0;
return 0;
}
int MyFunctorDense::df(const InputType &aX, JacobianType& aFjac)
{
for(int i = 0; i< aFjac.size(); i++)
{
aFjac(i, 0) = 2*i*aX(0);
aFjac(i, 1) = 2*i*(aX(1)-1);
}
return 0;
}
int main(int argc, char *argv[])
{
int input;
std::cout << "Enter 1 to run LM with DenseFunctor, Enter 2 to run LM with
SparseFunctor: " << std::endl;
std::cin >> input;
Eigen::VectorXd tX(2);
tX(0) = 10;
tX(1) = 0.5;
int tInputs = tX.rows();
int tValues = 60928;
std::cout << "tX: " << tX << std::endl;
if (input == 1)
{
MyFunctorDense myDenseFunctor;
tInputs = myDenseFunctor.inputs();
tValues = myDenseFunctor.values();
std::cout << "tInputs : " << tInputs << std::endl;
std::cout << "tValues : " << tValues << std::endl;
Eigen::LevenbergMarquardt<MyFunctorDense> lm(myDenseFunctor);
lm.setMaxfev(30);
lm.setXtol(1e-5);
lm.minimize(tX);
}
if (input == 2)
{
MyFunctorSparse myFunctorSparse;
//Eigen::NumericalDiff<MyFunctor> numDiff(myFunctor);
//Eigen::LevenbergMarquardt<Eigen::NumericalDiff<MyFunctor>,double>
lm(numDiff);
Eigen::LevenbergMarquardt<MyFunctorSparse> lm(myFunctorSparse);
lm.setMaxfev(2000);
lm.setXtol(1e-10);
lm.minimize(tX);
}
std::cout << "tX minimzed: " << tX << std::endl;
return 0;
}
Solution: I figured out my problem. I replaced:
const int m_inputs, m_values;
with
int m_inputs, m_values;
in the ".h" file this makes the member variable of the struct MyFunctorDense modifiable. So, then in the ".cpp" below the line
std::cout << "tX: " << tX << std::endl;
I added:
Eigen::VectorXd tF(60928);
because this is a test function vector of dimension 60928x1. Therefore, I could put in any arbitrary nx1 dimension.
Then below the line:
MyFunctorDense myDenseFunctor;
I added:
myDenseFunctor.m_inputs = tX.rows();
myDenseFunctor.m_values = tF.rows();
Now I get the result:

how to access vector of map of ( int and vector of strings )

how do i access map of int and vectors of string in the passed_vector function.
I just want to print them in that function.
#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;
typedef vector< map< int, vector<string> > > vmis;
typedef map< int, vector<string> > mis;
typedef vector<string> vstr;
void passing_vector(const vmis &meetings);
//return size of vector
template< typename A > size_t n_elements( const A& a )
{
return sizeof a / sizeof a[ 0 ];
}
int main()
{
vmis meeting_info;
mis meeting_members;
vstr sw_vec;
vstr sys_vec;
string sw_team[] = {"Ricky", "John", "David"};
string sys_team[] = {"Simmon", "Brad", "Schmidt", "Fizio"};
sw_vec.insert(sw_vec.begin(), sw_team, sw_team + n_elements(sw_team) );
sys_vec.insert(sys_vec.begin(), sys_team, sys_team + n_elements(sys_team) );
meeting_members.insert(make_pair(520, sw_vec));
meeting_members.insert(make_pair(440, sys_vec));
meeting_info.push_back(meeting_members);
passing_vector(meeting_info);
return 0;
}
void passing_vector(const vmis &meetings)
{
vmis::iterator itvmis = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
}
I know how to print them in main function.
vmis::iterator itvims = meeting_info.begin();
for( int i = 0; i < meeting_info.size(); i++ )
{
mis::iterator itm = meeting_members.begin();
for(itm; itm != meeting_members.end(); itm++ )
{
cout << itm->first << " : ";
vstr::iterator it = itm->second.begin();
for(it; it != itm->second.end(); it++)
cout << *it << " ";
cout << endl;
}
}
desired output
440 : Simmon Brad Schmidt Fizio
520 : Ricky John David
if there is a better way of doing this suggestions are always welcome.
The easiest aproach is to use auto, also since your meetings is const, you need to use const_iterator:
void passing_vector(const vmis &meetings)
{
vmis::const_iterator itvims = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
for (;itvims != meetings.end(); ++itvims)
{
const auto& map_item = *itvims;
for (const auto& map_it : map_item)
{
int map_key = map_it.first;
const auto& str_vec = map_it.second;
for (const auto& str : str_vec)
{
std::cout << map_key << " - " << str << "\n";
}
}
}
}
[edit]
c++98 version:
void passing_vector(const vmis &meetings)
{
vmis::const_iterator itvims = meetings.begin();
//how do i access map of int and vectors of string.
//I just want to print them.
for (;itvims != meetings.end(); ++itvims)
{
const mis& map_item = *itvims;
for (mis::const_iterator map_it = map_item.begin(); map_it != map_item.end(); ++map_it)
{
int map_key = map_it->first;
const vstr& str_vec = map_it->second;
for (vstr::const_iterator sitr = str_vec.begin(); sitr != str_vec.end(); ++sitr)
{
std::cout << map_key << " - " << *sitr << "\n";
}
}
}
}

Different return and coordinate types in nanoflann radius search

I'm trying to use nanoflann in a project and am looking at the vector-of-vector and radius search examples.
I can't find a way to perform a radius search with a different data type than the coordinate type. For example, my coordinates are vectors of uint8_t; I am trying to input a radius of type uint32_t with little success.
I see in the source that the metric_L2 struct (which I am using for distance) uses the L2_Adaptor with two template parameters. L2_Adaptor itself takes three parameters, with the third defaulted to the first, which seems to be the problem if I am understanding the code correctly. However, trying to force use of the third always results in 0 matches in the radius search.
Is there a way to do this?
Edit: In the same code below, everything works. However, if I change the search_radius (and ret_matches) to uint32_t, the radiusSearch method doesn't work.
#include <iostream>
#include <Eigen/Dense>
#include <nanoflann.hpp>
typedef Eigen::Matrix<uint8_t, Eigen::Dynamic, 1> coord_t;
using namespace nanoflann;
struct Point
{
coord_t address;
Point() {}
Point(uint8_t coordinates) : address(coord_t::Random(coordinates)) {}
};
struct Container
{
std::vector<Point> points;
Container(uint8_t coordinates, uint32_t l)
: points(l)
{
for(auto& each_location: points)
{
each_location = Point(coordinates);
}
}
};
struct ContainerAdaptor
{
typedef ContainerAdaptor self_t;
typedef nanoflann::metric_L2::traits<uint8_t, self_t>::distance_t metric_t;
typedef KDTreeSingleIndexAdaptor<metric_t, self_t, -1, size_t> index_t;
index_t *index;
const Container &container;
ContainerAdaptor(const int dimensions, const Container &container, const int leaf_max_size = 10)
: container(container)
{
assert(container.points.size() != 0 && container.points[0].address.rows() != 0);
const size_t dims = container.points[0].address.rows();
index = new index_t(dims, *this, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size));
index->buildIndex();
}
~ContainerAdaptor()
{
delete index;
}
inline void query(const uint8_t *query_point, const size_t num_closest, size_t *out_indices, uint32_t *out_distances_sq, const int ignoreThis = 10) const
{
nanoflann::KNNResultSet<uint32_t, size_t, size_t> resultSet(num_closest);
resultSet.init(out_indices, out_distances_sq);
index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
}
const self_t& derived() const
{
return *this;
}
self_t& derived()
{
return *this;
}
inline size_t kdtree_get_point_count() const
{
return container.points.size();
}
inline size_t kdtree_distance(const uint8_t *p1, const size_t idx_p2, size_t size) const
{
size_t s = 0;
for (size_t i = 0; i < size; i++)
{
const uint8_t d = p1[i] - container.points[idx_p2].address[i];
s += d * d;
}
return s;
}
inline coord_t::Scalar kdtree_get_pt(const size_t idx, int dim) const
{
return container.points[idx].address[dim];
}
template <class BBOX>
bool kdtree_get_bbox(BBOX & bb) const
{
for(size_t i = 0; i < bb.size(); i++)
{
bb[i].low = 0;
bb[i].high = UINT8_MAX;
}
return true;
}
};
void container_demo(const size_t points, const size_t coordinates)
{
Container s(coordinates, points);
coord_t query_pt(coord_t::Random(coordinates));
typedef ContainerAdaptor my_kd_tree_t;
my_kd_tree_t mat_index(coordinates, s, 25);
mat_index.index->buildIndex();
const uint8_t search_radius = static_cast<uint8_t>(100);
std::vector<std::pair<size_t, uint8_t>> ret_matches;
nanoflann::SearchParams params;
const size_t nMatches = mat_index.index->radiusSearch(query_pt.data(), search_radius, ret_matches, params);
for (size_t i = 0; i < nMatches; i++)
{
std::cout << "idx[" << i << "]=" << +ret_matches[i].first << " dist[" << i << "]=" << +ret_matches[i].second << std::endl;
}
std::cout << std::endl;
std::cout << "radiusSearch(): radius=" << +search_radius << " -> " << +nMatches << " matches" << std::endl;
}
int main()
{
container_demo(1e6, 32);
return 0;
}
More info: so it seems that the distance type, which the third parameter of the L2_Adaptor, must be a signed type. Changing the metric_t typedef to the following solves the problem if search_radius and ret_matches are also changed to int64_t.
typedef L2_Adaptor<uint8_t, self_t, int64_t> metric_t;

How to index and query STL map containers by multiple keys?

I came across one requirement where the record is stored as
Name : Employee_Id : Address
where Name and Employee_Id are supposed to be keys that is, a search function is to be provided on both Name and Employee Id.
I can think of using a map to store this structure
std::map< std:pair<std::string,std::string> , std::string >
// < < Name , Employee-Id> , Address >
but I'm not exactly sure how the search function will look like.
Boost.Multiindex
This is a Boost example
In the above example an ordered index is used but you can use also a hashed index:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <string>
#include <iostream>
struct employee
{
int id_;
std::string name_;
std::string address_;
employee(int id,std::string name,std::string address):id_(id),name_(name),address_(address) {}
};
struct id{};
struct name{};
struct address{};
struct id_hash{};
struct name_hash{};
typedef boost::multi_index_container<
employee,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<boost::multi_index::tag<id>, BOOST_MULTI_INDEX_MEMBER(employee,int,id_)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,std::string,name_)>,
boost::multi_index::ordered_unique<boost::multi_index::tag<address>, BOOST_MULTI_INDEX_MEMBER(employee,std::string,address_)>,
boost::multi_index::hashed_unique<boost::multi_index::tag<id_hash>, BOOST_MULTI_INDEX_MEMBER(employee,int,id_)>,
boost::multi_index::hashed_unique<boost::multi_index::tag<name_hash>, BOOST_MULTI_INDEX_MEMBER(employee,std::string,name_)>
>
> employee_set;
typedef boost::multi_index::index<employee_set,id>::type employee_set_ordered_by_id_index_t;
typedef boost::multi_index::index<employee_set,name>::type employee_set_ordered_by_name_index_t;
typedef boost::multi_index::index<employee_set,name_hash>::type employee_set_hashed_by_name_index_t;
typedef boost::multi_index::index<employee_set,id>::type::const_iterator employee_set_ordered_by_id_iterator_t;
typedef boost::multi_index::index<employee_set,name>::type::const_iterator employee_set_ordered_by_name_iterator_t;
typedef boost::multi_index::index<employee_set,id_hash>::type::const_iterator employee_set_hashed_by_id_iterator_t;
typedef boost::multi_index::index<employee_set,name_hash>::type::const_iterator employee_set_hashed_by_name_iterator_t;
int main()
{
employee_set employee_set_;
employee_set_.insert(employee(1, "Employer1", "Address1"));
employee_set_.insert(employee(2, "Employer2", "Address2"));
employee_set_.insert(employee(3, "Employer3", "Address3"));
employee_set_.insert(employee(4, "Employer4", "Address4"));
// search by id using an ordered index
{
const employee_set_ordered_by_id_index_t& index_id = boost::multi_index::get<id>(employee_set_);
employee_set_ordered_by_id_iterator_t id_itr = index_id.find(2);
if (id_itr != index_id.end() ) {
const employee& tmp = *id_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by non existing id using an ordered index
{
const employee_set_ordered_by_id_index_t& index_id = boost::multi_index::get<id>(employee_set_);
employee_set_ordered_by_id_iterator_t id_itr = index_id.find(2234);
if (id_itr != index_id.end() ) {
const employee& tmp = *id_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an ordered index
{
const employee_set_ordered_by_name_index_t& index_name = boost::multi_index::get<name>(employee_set_);
employee_set_ordered_by_name_iterator_t name_itr = index_name.find("Employer3");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an hashed index
{
employee_set_hashed_by_name_index_t& index_name = boost::multi_index::get<name_hash>(employee_set_);
employee_set_hashed_by_name_iterator_t name_itr = index_name.find("Employer4");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
// search by name using an hashed index but the name does not exists in the container
{
employee_set_hashed_by_name_index_t& index_name = boost::multi_index::get<name_hash>(employee_set_);
employee_set_hashed_by_name_iterator_t name_itr = index_name.find("Employer46545");
if (name_itr != index_name.end() ) {
const employee& tmp = *name_itr;
std::cout << tmp.id_ << ", " << tmp.name_ << ", " << tmp .address_ << std::endl;
} else {
std::cout << "No records have been found\n";
}
}
return 0;
}
If you want to use std::map, you can have two separate containers, each one having adifferent key (name, emp id) and the value should be a pointer the structure, so that you will not have multiple copies of the same data.
Example with tew keys:
#include <memory>
#include <map>
#include <iostream>
template <class KEY1,class KEY2, class OTHER >
class MultiKeyMap {
public:
struct Entry
{
KEY1 key1;
KEY2 key2;
OTHER otherVal;
Entry( const KEY1 &_key1,
const KEY2 &_key2,
const OTHER &_otherVal):
key1(_key1),key2(_key2),otherVal(_otherVal) {};
Entry() {};
};
private:
struct ExtendedEntry;
typedef std::shared_ptr<ExtendedEntry> ExtendedEntrySptr;
struct ExtendedEntry {
Entry entry;
typename std::map<KEY1,ExtendedEntrySptr>::iterator it1;
typename std::map<KEY2,ExtendedEntrySptr>::iterator it2;
ExtendedEntry() {};
ExtendedEntry(const Entry &e):entry(e) {};
};
std::map<KEY1,ExtendedEntrySptr> byKey1;
std::map<KEY2,ExtendedEntrySptr> byKey2;
public:
void del(ExtendedEntrySptr p)
{
if (p)
{
byKey1.erase(p->it1);
byKey2.erase(p->it2);
}
}
void insert(const Entry &entry) {
auto p=ExtendedEntrySptr(new ExtendedEntry(entry));
p->it1=byKey1.insert(std::make_pair(entry.key1,p)).first;
p->it2=byKey2.insert(std::make_pair(entry.key2,p)).first;
}
std::pair<Entry,bool> getByKey1(const KEY1 &key1)
{
const auto &ret=byKey1[key1];
if (ret)
return std::make_pair(ret->entry,true);
return std::make_pair(Entry(),false);
}
std::pair<Entry,bool> getByKey2(const KEY2 &key2)
{
const auto &ret=byKey2[key2];
if (ret)
return std::make_pair(ret->entry,true);
return std::make_pair(Entry(),false);
}
void deleteByKey1(const KEY1 &key1)
{
del(byKey1[key1]);
}
void deleteByKey2(const KEY2 &key2)
{
del(byKey2[key2]);
}
};
int main(int argc, const char *argv[])
{
typedef MultiKeyMap<int,std::string,int> M;
M map1;
map1.insert(M::Entry(1,"aaa",7));
map1.insert(M::Entry(2,"bbb",8));
map1.insert(M::Entry(3,"ccc",9));
map1.insert(M::Entry(7,"eee",9));
map1.insert(M::Entry(4,"ddd",9));
map1.deleteByKey1(7);
auto a=map1.getByKey1(2);
auto b=map1.getByKey2("ddd");
auto c=map1.getByKey1(7);
std::cout << "by key1=2 (should be bbb ): "<< (a.second ? a.first.key2:"Null") << std::endl;
std::cout << "by key2=ddd (should be ddd ): "<< (b.second ? b.first.key2:"Null") << std::endl;
std::cout << "by key1=7 (does not exist): "<< (c.second ? c.first.key2:"Null") << std::endl;
return 0;
}
Output:
by key1=2 (should be bbb ): bbb
by key2=ddd (should be ddd ): ddd
by key1=7 (does not exist): Null
If EmployeeID is the unique identifier, why use other keys? I would use EmployeeID as the internal key everywhere, and have other mappings from external/human readable IDs (such as Name) to it.
C++14 std::set::find non-key searches solution
This method saves you from storing the keys twice, once one the indexed object and secondly on as the key of a map as done at: https://stackoverflow.com/a/44526820/895245
This provides minimal examples of the central technique that should be easier to understand first: How to make a C++ map container where the key is part of the value?
#include <cassert>
#include <set>
#include <vector>
struct Point {
int x;
int y;
int z;
};
class PointIndexXY {
public:
void insert(Point *point) {
sx.insert(point);
sy.insert(point);
}
void erase(Point *point) {
sx.insert(point);
sy.insert(point);
}
Point* findX(int x) {
return *(this->sx.find(x));
}
Point* findY(int y) {
return *(this->sy.find(y));
}
private:
struct PointCmpX {
typedef std::true_type is_transparent;
bool operator()(const Point* lhs, int rhs) const { return lhs->x < rhs; }
bool operator()(int lhs, const Point* rhs) const { return lhs < rhs->x; }
bool operator()(const Point* lhs, const Point* rhs) const { return lhs->x < rhs->x; }
};
struct PointCmpY {
typedef std::true_type is_transparent;
bool operator()(const Point* lhs, int rhs) const { return lhs->y < rhs; }
bool operator()(int lhs, const Point* rhs) const { return lhs < rhs->y; }
bool operator()(const Point* lhs, const Point* rhs) const { return lhs->y < rhs->y; }
};
std::set<Point*, PointCmpX> sx;
std::set<Point*, PointCmpY> sy;
};
int main() {
std::vector<Point> points{
{1, -1, 1},
{2, -2, 4},
{0, 0, 0},
{3, -3, 9},
};
PointIndexXY idx;
for (auto& point : points) {
idx.insert(&point);
}
Point *p;
p = idx.findX(0);
assert(p->y == 0 && p->z == 0);
p = idx.findX(1);
assert(p->y == -1 && p->z == 1);
p = idx.findY(-2);
assert(p->x == 2 && p->z == 4);
}