Related
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/random_access_index.hpp>
#include <boost/multi_index/member.hpp>
#include <string>
#include <iostream>
//#include <boost/multi_index/identity.hpp>
using namespace boost::multi_index;
//tags
struct NR_PCI {};
struct NR_FREQ_REF {};
struct NR_MOID {};
struct nr_cell_relation
{
uint16_t id;
uint16_t nr_freq_ref;
uint16_t pci;
uint64_t nrCellIdentity;
};
typedef multi_index_container<
nr_cell_relation,
indexed_by<
sequenced<>,
// ordered_unique<identity<nr_cell_relation> >,
ordered_unique< tag<NR_PCI>, member< nr_cell_relation, uint16_t, &nr_cell_relation::pci > >,
ordered_unique< tag<NR_MOID>, member< nr_cell_relation, uint16_t, &nr_cell_relation::id > > > > nr_anr_nrt_t;
nr_anr_nrt_t nr_anr_nrt;
bool pci_entry_exits(uint16_t pci)
{
auto &pci_index = nr_anr_nrt.get<NR_PCI>();
auto itr = pci_index.find(pci);
if (itr != pci_index.end())
{
std::cout<<"PCI MAP Entry Exist: PCI"<<itr->pci<<std::endl;
return true;
}
std::cout<<"PCI MAP Entry Not Exist: PCI"<<pci<<std::endl;
return false;
}
bool mo_id_entry_exits(uint16_t id)
{
auto &pci_index = nr_anr_nrt.get<NR_MOID>();
auto itr = pci_index.find(id);
if (itr != pci_index.end())
{
std::cout<<"MOID Entry Exist: MOID:"<<itr->id<<":PCI:"<<itr->pci<<std::endl;
return true;
}
std::cout<<"MOID Entry Not Exist: MOID"<<id<<std::endl;
return false;
}
int main()
{
if (!pci_entry_exits(304))
{
nr_anr_nrt.push_back({1, 2, 304, 1234566});
}
if (!pci_entry_exits(301))
{
nr_anr_nrt.push_back({4, 1, 301, 1234567});
}
if (!pci_entry_exits(303))
{
nr_anr_nrt.push_back({2, 2, 303, 1234569});
}
if (!pci_entry_exits(302))
{
nr_anr_nrt.push_back({3, 1, 302, 1234568});
}
if (!pci_entry_exits(302))
{
nr_anr_nrt.push_back({5, 1, 302, 1234568});
}
if (mo_id_entry_exits(4))
{
nr_anr_nrt.push_back({4, 1, 302, 1234568});
}
std::cout<<"NRT DUMP with PCI KEY:"<<std::endl;
auto it = nr_anr_nrt.get<NR_PCI>().begin();
for (; it != nr_anr_nrt.get<NR_PCI>().end(); ++it)
{
std::cout << "MOID:"<<it->id<<"\tFreq_Ref:"<<it->nr_freq_ref<<"\tNR_PCI:"<<it->pci<<std::endl;
}
std::cout<<"NRT DUMP with MOID KEY:"<<std::endl;
auto itr = nr_anr_nrt.get<NR_MOID>().begin();
for (; itr != nr_anr_nrt.get<NR_MOID>().end(); ++itr)
{
std::cout << "MOID:"<<itr->id<<"\tFreq_Ref:"<<itr->nr_freq_ref<<"\tNR_PCI:"<<itr->pci<<std::endl;
}
std::cout<<"NRT DUMP ORDER OF INSERTION:"<<std::endl;
auto it_r = nr_anr_nrt.begin();
for (; it_r != nr_anr_nrt.end(); ++it_r)
{
std::cout << "MOID:"<<it_r->id<<"\tFreq_Ref:"<<it_r->nr_freq_ref<<"\tNR_PCI:"<<it_r->pci<<std::endl;
}
}
Description:
The program is implemented for accessing multi index container using single unique key.
Problem statement: how to implement, whenever situation arises to access multi_index container using multiple keys like std::pair<key1, key2> or even tuple like std::tuple<key1, key2, key3> using composite_key.
Yes. You're looking for composite keys. In your example, e.g.
bmi::ordered_unique<bmi::composite_key<
bmi::key<&nr_cell_relation::id>, bmi::key<&nr_cell_relation::nr_freq_ref>,
bmi::key<&nr_cell_relation::pci>,
bmi::key<&nr_cell_relation::nrCellIdentity>>>,
In the case of ordered indexes you can even query with partial keys (most significant left-to-right, so lexicographically).
Here's a rather contrived demo:
Live On Coliru
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/key.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
namespace Lib {
struct Relation {
uint16_t id;
uint16_t nr_freq_ref;
uint16_t pci;
uint64_t nrCellIdentity;
};
struct NR_PCI;
struct NR_MOID;
namespace detail {
using namespace boost::multi_index;
using nr_anr_nrt_t = multi_index_container< //
Relation, //
indexed_by< //
sequenced<>, //
ordered_unique<composite_key< //
Relation, //
key<&Relation::nr_freq_ref>, //
key<&Relation::id>, //
key<&Relation::pci>, //
key<&Relation::nrCellIdentity> //
>>, //
ordered_unique<tag<NR_PCI>, key<&Relation::pci>>, //
ordered_unique<tag<NR_MOID>, key<&Relation::id>>>>; //
} // namespace detail
using detail::nr_anr_nrt_t;
} // namespace Lib
#include <boost/pfr.hpp> // only for debug output
#include <boost/range/iterator_range.hpp>
#include <iostream>
BOOST_PFR_FUNCTIONS_FOR(Lib::Relation)
void dump(auto& os, auto const& table) {
for (auto& rec : boost::make_iterator_range(table))
os << " * " << rec << "\n";
}
int main() {
using namespace Lib;
std::cout << "(id, freqref, pci, nrcellid)\n";
nr_anr_nrt_t nr_anr_nrt{
{4, 9990, 11, 33},
{1, 3330, 44, 99},
{3, 5550, 22, 77},
{2, 5550, 33, 55},
};
dump(std::cout << "sequenced:\n", nr_anr_nrt);
dump(std::cout << "pci:\n", nr_anr_nrt.get<NR_PCI>());
dump(std::cout << "moid:\n", nr_anr_nrt.get<NR_MOID>());
auto& composite = nr_anr_nrt.get<1>(); // untagged index by integer id
using boost::make_tuple;
dump(std::cout << "composite freqref 3330:\n",
composite.equal_range(3330));
dump(std::cout << "composite freqref 5550:\n",
composite.equal_range(5550));
dump(std::cout << "composite (5550, 2):\n",
composite.equal_range(make_tuple(5550, 2)));
dump(std::cout << "composite (5550, 3):\n",
composite.equal_range(make_tuple(5550, 3)));
auto f = composite.lower_bound(1);
auto l = composite.upper_bound(make_tuple(5550, 2));
dump(std::cout << "composite range:\n", std::pair(f, l));
}
Prints
(id, freqref, pci, nrcellid)
sequenced:
* {4, 9990, 11, 33}
* {1, 3330, 44, 99}
* {3, 5550, 22, 77}
* {2, 5550, 33, 55}
pci:
* {4, 9990, 11, 33}
* {3, 5550, 22, 77}
* {2, 5550, 33, 55}
* {1, 3330, 44, 99}
moid:
* {1, 3330, 44, 99}
* {2, 5550, 33, 55}
* {3, 5550, 22, 77}
* {4, 9990, 11, 33}
composite freqref 3330:
* {1, 3330, 44, 99}
composite freqref 5550:
* {2, 5550, 33, 55}
* {3, 5550, 22, 77}
composite (5550, 2):
* {2, 5550, 33, 55}
composite (5550, 3):
* {3, 5550, 22, 77}
composite range:
* {1, 3330, 44, 99}
* {2, 5550, 33, 55}
#include <algorithm>
#include <array>
#include <iostream>
int main() {
std::array<int, 10> s{5, 7, 4, 2, 8, 6, 1, 9, 0, 3};
struct {
bool operator()(int a, int b) const
{
return a < b;
}
} customLess;
std::sort(s.begin(), s.end(), customLess);
for (auto a : s) {
std::cout << a << " ";
}
std::cout << '\n';
return 0;
}
I want to sort the array using std : : sort. The code above works fine, but when I try with template array in my main task (GArr<int, 5> arr{5, 2, 3, 4, 1};) the compiler gives the following
error: no match for 'operator-' (operand types are 'Iterator' and
'Iterator')|
How can I fix it?
How can I initialize array with variables like int x[row][col];
int myArray[7][4] = { {1,2,3,4}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8}, {5,6,7,8} };
i want to initialize array as this =
int myarray({2,3,4},{12,4,5},{2,2,2})
The exact answer is you cannot initialize an array like that, i.e., without providing both row and col at compile time, though std::vector can do the job for you.
You can use some code like this:
#include <iostream>
#include <vector>
void print_vector(std::vector<int> &v) {
std::cout << "{ ";
for (auto &&i : v) {
std::cout << i;
if (&i != &v.back()) {
std::cout << ",";
}
}
std::cout << " }";
}
void print_matrix(std::vector<std::vector<int>> &v) {
std::cout << "{ ";
for (auto &&i : v) {
print_vector(i);
if (&i != &v.back()) {
std::cout << ", ";
}
}
std::cout << " }" << std::endl;
}
int main() {
std::vector<std::vector<int>> v = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
// same as std::vector<std::vector<int>> v({{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}});
print_matrix(v);
// prints { { 1,2,3,4 }, { 5,6,7,8 }, { 9,10,11,12 } } on stdout
}
I have included print_vector and print_matrix since the OP asked about them in the comments, though without thinking much about them. You can get better implementations on this thread.
pretty print container of arbitrary type
The following code of few lines provides same output as prettyprint.hpp in question,
but the output stream is limited to std::cout.
How to rewrite those code using boost::hof to provide print(std::ostream&, ...) like interface?
#include <iostream>
#include <boost/hof.hpp>
BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print) = //boost::hof::proj(
boost::hof::fix(boost::hof::first_of(
[](auto, const auto& x) -> decltype(std::cout << x, void()) {
std::cout << x;
},
[](auto self, const auto& range)
-> decltype(self(*std::begin(range)), void()) {
bool sep = false;
std::cout << '{';
for (const auto& x : range) {
if (sep)
std::cout << ',';
else
sep = true;
self(x);
}
std::cout << '}';
},
[](auto self, const auto& tuple) {
using namespace boost::hof;
std::cout << '(';
bool sep = false;
unpack(proj([&](const auto& i) {
if (sep)
std::cout << ',';
else
sep = true;
self(i);
}))(tuple);
std::cout << ')';
}));//})));
template <typename... Args>
void print(Args&&... args) {
simple_print(std::make_tuple(std::forward<Args>(args)...));
}
//---- user code ---
struct XX {
int n = 0;
friend std::ostream& operator<<(std::ostream& os, const XX& o) {
return os << o.n << "XX";
}
};
int main() {
std::vector v = {1, 2, 3, 4};
std::map<std::string, int> m = {{"a", 30}, {"bb", 31}, {"ccc", 32}};
auto t = std::make_tuple(6, 7, 8, 9);
auto t2 = std::make_tuple(11, std::ref(v), t);
auto t3 = std::make_tuple(t2, std::vector{1234, 23, 2, 3, 3}, "abc",
std::vector{
std::vector{11, 12, 13}, std::vector{15, 16, 17}, std::vector{19}});
print(t3, "xxxx", 55, m, std::vector<std::string>{"x"}, XX{66});
// (((11, [1, 2, 3, 4], (6, 7, 8, 9)), [1234, 23, 2, 3, 3], abc, [[11, 12,
// 13], [15, 16, 17], [19]]), xxxx, 55, [(a, 30), (bb, 31), (ccc, 32)], [x],
// 66XX)
}
Just adding a parameter to pass the ostream would suffice:
BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print_ex) = boost::hof::fix(
boost::hof::first_of(
[](auto, auto& os, const auto &x) -> decltype(os << x, void()) { os << x; },
[](auto self, auto& os, const auto &range) -> decltype(self(os, *std::begin(range)), void()) {
bool sep = false;
os << '{';
for (const auto &x : range) {
sep = !sep || os << ',';
self(os, x);
}
os << '}';
},
[](auto self, auto& os, const auto &tuple) {
using namespace boost::hof;
os << '(';
bool sep = false;
unpack(proj([&](const auto &i) {
sep = !sep || os << ',';
self(os, i);
}))(tuple);
os << ')';
}));
template <typename Ostream, typename... Args> void print_ex(Ostream& os, Args &&... args) { simple_print_ex(os, std::make_tuple(std::forward<Args>(args)...)); }
Now you can use it like so:
Live On Wandbox
std::ofstream ofs("test.txt");
print_ex(ofs, t3, "xxxx", 55, m, std::vector<std::string>{ "x" }, XX{ 66 });
ofs << "\n";
Of course, the old print can be a trivial forwarding wrapper now:
template <typename... Args> void print(Args &&... args) {
print_ex(std::cout, std::forward<Args>(args)...);
}
Listing
Live On Wandbox
#include <boost/hof.hpp>
#include <iostream>
BOOST_HOF_STATIC_LAMBDA_FUNCTION(simple_print_ex) = boost::hof::fix(
boost::hof::first_of(
[](auto, auto& os, const auto &x) -> decltype(os << x, void()) { os << x; },
[](auto self, auto& os, const auto &range) -> decltype(self(os, *std::begin(range)), void()) {
bool sep = false;
os << '{';
for (const auto &x : range) {
sep = !sep || os << ',';
self(os, x);
}
os << '}';
},
[](auto self, auto& os, const auto &tuple) {
using namespace boost::hof;
os << '(';
bool sep = false;
unpack(proj([&](const auto &i) {
sep = !sep || os << ',';
self(os, i);
}))(tuple);
os << ')';
}));
template <typename Ostream, typename... Args> void print_ex(Ostream& os, Args &&... args) { simple_print_ex(os, std::make_tuple(std::forward<Args>(args)...)); }
template <typename... Args> void print(Args &&... args) { print_ex(std::cout, std::forward<Args>(args)...); }
//---- user code ---
struct XX {
int n = 0;
friend std::ostream &operator<<(std::ostream &os, const XX &o) { return os << o.n << "XX"; }
};
#include <map>
#include <vector>
#include <fstream>
int main() {
using namespace std::string_literals;
std::vector v = { 1, 2, 3, 4 };
std::map m { std::pair { "a"s, 30 }, { "bb", 31 }, { "ccc", 32 } };
auto t = std::make_tuple(6, 7, 8, 9);
auto t2 = std::make_tuple(11, std::ref(v), t);
auto t3 = std::make_tuple(t2, std::vector{ 1234, 23, 2, 3, 3 }, "abc",
std::vector{ std::vector{ 11, 12, 13 }, std::vector{ 15, 16, 17 }, std::vector{ 19 } });
std::ofstream ofs("test.txt");
print_ex(ofs, t3, "xxxx", 55, m, std::vector<std::string>{ "x" }, XX{ 66 });
ofs << "\n";
print(t3, "xxxx", 55, m, std::vector<std::string>{ "x" }, XX{ 66 });
}
I want to access to some class data using operator[] but depending on the index type into the square brackets return one kind of data or other. As a simplified example:
struct S
{
int &operator []( int index ) { std::cout << "[i]"; return i_buffer[index]; }
short &operator [](short index) { std::cout << "[s]"; return s_buffer[index]; }
private:
int i_buffer[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
short s_buffer[10]{ 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
};
There's no way to write a short literal, so the only way to choose the short overload is by casting:
S s;
std::cout << s[9] << '\n'; // prints [i]9
std::cout << s[(short)9] << '\n'; // prints [s]999
But I don't like it and I was wondering if there's different options.
What I've tried?
Tagged parameter.
First I've tried to use "tags":
struct S
{
enum class i_type : std::int32_t {};
enum class s_type : std::int32_t {};
int &operator [](i_type index)
{ std::cout << "[i]"; return i_buffer[static_cast<int>(index)]; }
short &operator [](s_type index)
{ std::cout << "[s]"; return s_buffer[static_cast<int>(index)]; }
private:
int i_buffer[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
short s_buffer[10]{ 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
};
That works but is still a little verbose:
S s;
std::cout << s[9] << '\n'; // error, no possible overload to be taken
std::cout << s[S::i_type{9}] << '\n'; // prints [i]9
std::cout << s[S::s_type{9}] << '\n'; // prints [s]999
Template.
As a crazy workaround I wanted to try to template the operator:
struct S
{
template <typename T>
T &operator [](T) { std::cout << "???"; return 0; }
private:
int i_buffer[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
short s_buffer[10]{ 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
};
template <>
int &S::operator [](int index) { std::cout << "[i]"; return i_buffer[index]; }
template <>
short &S::operator [](short index) { std::cout << "[s]"; return s_buffer[index]; }
The template version behaves as the original code, but there's no easy way to specify a type parameter along with the operator[]:
S s;
std::cout << s[9] << '\n'; // prints [i]9 like before
std::cout << s[(short)9] << '\n'; // prints [s]999 like before
std::cout << s<short>[9] << '\n'; // s is not template
std::cout << s[9]<short> << '\n'; // nonsense
// Correct but utterly verbose and hard to write and read
std::cout << s.operator[]<short>(9) << '\n';
Question.
All the issues described also happens with operator(), I want to know if there's more alternatives that I'm not aware of?
I think that using a named method is a much better idea than using operator[] in your situation, as it would be easier to understand that two separate buffers are being accessed by reading the source code.
Regardless, if you want to use your operator[] approach, you could use strong typedefs and user defined literals to have type-safety with minimal syntactic overhead:
BOOST_STRONG_TYPEDEF(std::size_t, int_index)
BOOST_STRONG_TYPEDEF(std::size_t, short_index)
struct S
{
auto& operator[](int_index i) { /* ... */ }
auto& operator[](short_index i) { /* ... */ }
};
auto operator "" _ii(unsigned long long int x) { return int_index{x}; }
auto operator "" _si(unsigned long long int x) { return short_index{x}; }
You can then call your methods as follows:
S s;
auto& some_int = s[15_ii];
auto& some_short = s[4_si];
wandbox example
I think I'd use std::tie from the <tuple> library and then write a little helper to find the correct reference type:
#include <tuple>
#include <iostream>
template<class As, class...Ts>
auto& as(std::tuple<const Ts&...>ts)
{
return std::get<As const&>(ts);
};
template<class As, class...Ts>
auto& as(std::tuple<Ts&...>ts)
{
return std::get<As &>(ts);
};
struct S
{
// both cost and mutable version provided for completeness.
auto operator[](std::size_t i) const {
return std::tie(i_buffer[i], s_buffer[i]);
}
auto operator[](std::size_t i) {
return std::tie(i_buffer[i], s_buffer[i]);
}
private:
int i_buffer[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
short s_buffer[10]{ 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
};
int main()
{
auto s = S();
const auto x = S();
std::cout << "short is : " << as<short>(s[5])<< '\n';
std::cout << "int is : " << as<int>(s[5])<< '\n';
std::cout << "short is : " << as<short>(x[6])<< '\n';
std::cout << "int is : " << as<int>(x[6])<< '\n';
}
This way, the code is explicit but still succinct.
expected output:
short is : 555
int is : 5
short is : 666
int is : 6
Having read the further comments, I might choose to store the matrix in (say) row-wise form and then provide a col-wise wrapper.
A barely functional example:
#include <tuple>
#include <iostream>
#include <array>
template<std::size_t Rows, std::size_t Cols>
struct RowWiseMatrix
{
auto& operator[](std::size_t i) { return data_[i]; }
std::array<std::array<double, Cols>, Rows> data_;
};
template<std::size_t Rows, std::size_t Cols>
struct ColumnProxy
{
ColumnProxy(std::array<std::array<double, Cols>, Rows>& data, std::size_t col)
: data_(data), col_(col)
{
}
auto& operator[](std::size_t i) { return data_[i][col_]; }
std::array<std::array<double, Cols>, Rows>& data_;
std::size_t col_;
};
template<std::size_t Rows, std::size_t Cols>
struct ColWiseProxy
{
ColWiseProxy(RowWiseMatrix<Rows, Cols>& mat) : underlying_(mat) {}
auto operator[](std::size_t i) { return ColumnProxy<Rows, Cols> { underlying_.data_, i }; }
RowWiseMatrix<Rows, Cols>& underlying_;
};
template<std::size_t Rows, std::size_t Cols>
auto& rowWise(RowWiseMatrix<Rows, Cols>& mat)
{
return mat;
};
template<std::size_t Rows, std::size_t Cols>
auto colWise(RowWiseMatrix<Rows, Cols>& mat)
{
return ColWiseProxy<Rows, Cols>(mat);
};
int main()
{
auto m = RowWiseMatrix<3, 3> {
std::array<double, 3>{ 1, 2, 3 },
std::array<double, 3>{ 4, 5, 6},
std::array<double, 3>{ 7, 8, 9}
};
std::cout << rowWise(m)[0][2] << '\n';
std::cout << colWise(m)[0][2] << '\n';
}
Expected output:
3
7
I agree with Vittorio Romeo that the best solution is a named method.
However here is a solution:
template <class T> struct S_proxy {
T* data;
T& operator[](std::size_t i) { return data[i]; }
};
struct S
{
auto i_type() { return S_proxy<int>{i_buffer}; };
auto s_type() { return S_proxy<short>{s_buffer}; };
private:
int i_buffer[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
short s_buffer[10]{ 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
};
and use:
S s;
return s.s_type()[2];
If i_type and s_type are supposed to have a meaning by themselves, it is possible to add semantics to operators []. Something like
#include <iostream>
struct Month {
explicit Month(int m)
: m(m)
{
}
int m;
};
struct Day {
explicit Day(short d)
: d(d)
{
}
short d;
};
struct S {
int& operator[](const Month& mes)
{
std::cout << "[i]";
return i_bufer[mes.m];
}
short& operator[](const Day& dis)
{
std::cout << "[s]";
return s_bufer[dis.d];
}
private:
int i_bufer[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
short s_bufer[10]{ 0, 111, 222, 333, 444, 555, 666, 777, 888, 999 };
};
int main()
{
S s;
std::cout << s[Month(9)] << '\n'; // muestra [i]9
std::cout << s[Day(9)] << '\n'; // muestra [s]999
}