[MultiIndex] Indexing integers
Dear all,
I am experimenting with MultiIndex, and I am happy with my custom classes.
Now I am wondering, can I index integers without a custom class?
For instance, say we have a 32 bit unsigned integer, how can I index
these with respect to the MSB or LSB? My first guess is using a union,
something like
union uint32_u
{
uint32_t data;
uint16_t bits[2];
};
// define a multiply indexed set with indices by id and name
typedef boost::multi_index::multi_index_container<
uint32_u,
boost::multi_index::indexed_by<
// sort by MSB
boost::multi_index::ordered_non_unique
integer_set;
But I cannot obviously compile it. Is there a way I can do this? Thanks!
Sensei
Dear all,
I am experimenting with MultiIndex, and I am happy with my custom classes.
Now I am wondering, can I index integers without a custom class?
For instance, say we have a 32 bit unsigned integer, how can I index these with respect to the MSB or LSB? [...]
You can define a function that does the MSB/LSB extraction and either
pack it as a custom key extractor:
http://www.boost.org/libs/multi_index/doc/
tutorial/key_extraction.html#user_defined_key_extractors
or plug it into boost::multi_index::global_fun:
http://www.boost.org/libs/multi_index/doc/
tutorial/key_extraction.html#global_fun
The following example shows the latter approach for a multi_index_container
of uint32_t's --you can easily adapt this to your particular scenario
where the uint32_t's are members of a larger class.
#include
multi_t;
int main()
{
multi_t m={0x00030000ul,0x00020001ul,0x00010002ul,0x00000003ul};
std::cout<
Thanks Joaquin, I've played with your example and I find it very useful!
Now the appetite grew and combining your example with the custom key
extractor docs you provided, I am trying to move to a single class with
a multi index member. However, I cannot compile it.
My attempt is now to use a 128bit integers, and extract MSB/LSB as
before. The difference is using a const member function. Of course, I've
specialized std::less for my type.
The errors are puzzling to me. Here they are:
== LOG ==
In file included from Data.h:15:
/usr/local/include/boost/multi_index/mem_fun.hpp:42:50: error: member
pointer refers into non-class type 'unsigned __int128'
template
Sensei
[...]
My attempt is now to use a 128bit integers, and extract MSB/LSB as before. The difference is using a const member function. Of course, I've specialized std::less for my type.
This is off-topic, but you don't need to specialize std::less for __uint128_t because the default implementation does exactly the same as your specialization (both use operator<).
[...]
From the documentation I see that I need in const_mem_fun<> in order, the input type, the output type, and the address of a member function, a const member in my case.
const_mem_fun expects a member function *of the class being inserted
into the container* (which in your case is __uint128_t), not just
any member function of some unrelated class (in your case, you're
trying to plug &Data::msb and &Data::lsb).
So, turn &Data::msb and &Data::lsb into *static* member functions
(as they're really global functions, not needing to access any
member of Data):
static uint64_t msb(__uint128_t storage)
{
return static_cast
On 7/20/14, 12:55pm, Joaquin M Lopez Munoz wrote:
This is off-topic, but you don't need to specialize std::less for __uint128_t because the default implementation does exactly the same as your specialization (both use operator<). [...]
Thanks Joaquin, and sorry for the OT. I thought it was a problem regarding std::less. Thanks again!
As Joaquín helped me out, I'm currently using his solution to test multi
index containers.
Playing with that, I'm now moving to hashed indices and 128 bits
integers, just for fun.
So now, in my class Data, I have a 128bit integer, indexed by
most/half/least significant bits, with access functions. It works
nicely. I added a fourth index, a hashed one, hashed by the MSB, and now
I'd like to access all items that have MSB equal to 3, for instance.
You will find my badly-written code below.
Following the example [1], I proceeded in defining the index type, but
here my compiler throws me two errors:
error: missing 'typename' prior to dependent type name
'DataWithIndices::nth_index'
^
and
error: expected unqualified-id
typedef DataWithIndices::nth_index<3>::type byHashedIndex;
^
So I followed the suggestion and used typename, but an error is there:
error: use 'template' keyword to treat 'nth_index' as a dependent
template name
typedef typename DataWithIndices::nth_index<3>::type byHashedIndex;
^
Again, I follow the suggestion, use template and it works. Adding the
following line from the example gives me an error:
error: expected expression
byHashedIndex &p = multiIndex_.get<3>();
^
error: declaration of reference variable 'p' requires an initializer
byHashedIndex &p = multiIndex_.get<3>();
^
Sincerely, I think I'm translating the example in a right manner, but
obviously I am not.
Can you help me? I am learning still, so please excuse me if this is
trivial!
Thanks & Cheers!
=== CODE ===
typedef boost::multi_index::multi_index_container<
__uint128_t,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique
Sensei
As Joaquín helped me out, I'm currently using his solution to test multi index containers.
[...]
Following the example [1], I proceeded in defining the index type, but here my compiler throws me two errors:
error: missing 'typename' prior to dependent type name 'DataWithIndices::nth_index' ^
and
error: expected unqualified-id typedef DataWithIndices::nth_index<3>::type byHashedIndex; ^
So I followed the suggestion and used typename, but an error is there:
error: use 'template' keyword to treat 'nth_index' as a dependent template name typedef typename DataWithIndices::nth_index<3>::type byHashedIndex; ^
Again, I follow the suggestion, use template and it works. Adding the following line from the example gives me an error:
error: expected expression byHashedIndex &p = multiIndex_.get<3>(); ^
error: declaration of reference variable 'p' requires an initializer byHashedIndex &p = multiIndex_.get<3>();
I think you need to write byHashedIndex &p = multiIndex_.template get<3>(); (note the extra "template"). You might want to check the excellent explanation on templates and dependent names given at http://tinyurl.com/cmlqhcr . Best, Joaquín M López Muñoz Telefónica
On 7/29/14, 5:53 PM, Joaquin M Lopez Munoz wrote:
I think you need to write
byHashedIndex &p = multiIndex_.template get<3>();
(note the extra "template"). You might want to check the excellent explanation on templates and dependent names given at http://tinyurl.com/cmlqhcr .
Thanks as always Joaquin, this solves the compilation error. I might have misunderstood how hashed indices work, because I don't know if multi index is suitable for fast retrieval of, for instance, all 128-bit integers that have MSB equal to 3? Something akin to a simple std::unordered_multimap with better storage? It's a fairly complex library, I must admit :) Thanks!
Sensei
On 7/29/14, 5:53 PM, Joaquin M Lopez Munoz wrote:
I think you need to write
byHashedIndex &p = multiIndex_.template get<3>();
(note the extra "template"). You might want to check the excellent explanation on templates and dependent names given at http://tinyurl.com/cmlqhcr .
Thanks as always Joaquin, this solves the compilation error.
I might have misunderstood how hashed indices work, because I don't know if multi index is suitable for fast retrieval of, for instance, all 128-bit integers that have MSB equal to 3? Something akin to a simple std::unordered_multimap with better storage?
Absolutely. The interface of hashed indices is modelled upon that of C++ unordered associative containers. equal_range(3) (on the MSB index) will get you the elements you're after. Joaquín M López Muñoz Telefonica
On 7/30/14, 5:17pm, Joaquin M Lopez Munoz wrote:
Absolutely. The interface of hashed indices is modelled upon that of C++ unordered associative containers. equal_range(3) (on the MSB index) will get you the elements you're after.
Perfect! So I am doing something wrong with a library that does what I'd
like!
The following is a simple dump & append to list. The objective is
simple, append to a list all 128-bit integers that have a common MSB. To
debug I've also dumped the whole container, just to be sure.
My code, actually, won't find anything with equal_range, and find will
return 0, although my call works in some sense, or better, the element I
am passing DOES exist in the container. Source and output in the following.
Apart from feeling shame over my flawed code, I'd like to know some
underlying facts, if possible.
The complexity of equal_range is constant with respect to the key I'm
finding, in other words, linear wrt the number of matched keys. Is this
always the average case regardless of the number of elements in the
container?
If I need to find, for instance, all items matching MSB, and within
them, all matching HSB, will I need an additional key (composite key, if
I understand)? It seems quite expensive from the memory point of view,
so in case I have may items I could just iterate over them.
Instead of copying matched items, is it possible to have a
"sub-container" akin to a view, without copying anything? Probably not...
Thanks for your valuable help!
Cheers!
=== SOURCE ===
typedef boost::multi_index::multi_index_container<
__uint128_t,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique
Sensei
On 7/30/14, 5:17pm, Joaquin M Lopez Munoz wrote:
Absolutely. The interface of hashed indices is modelled upon that of C++ unordered associative containers. equal_range(3) (on the MSB index) will get you the elements you're after.
Perfect! So I am doing something wrong with a library that does what I'd like!
The following is a simple dump & append to list. The objective is simple, append to a list all 128-bit integers that have a common MSB. To debug I've also dumped the whole container, just to be sure.
My code, actually, won't find anything with equal_range, and find will return 0, although my call works in some sense, or better, the element I am passing DOES exist in the container. Source and output in the following.
OK, some weird things are happening here, though I can only guess as the code you're showing is not complete. Seemingly p.equal_range(v) is returning an empty result (since the output does not emit any EQID). As for p.find(v) returning something, what's actually hapenning is that it is returning an iterator to end --this is why FIND outputs 0|0|0 when there's no such element in the container, judging from the ALL dump. This you can easily check by comparing u with p.end(). So... my wild guess is that Data::MSB is not working properly. Could you please show the code for that function? (even better, could you post a complete compilable snippet for me to play with?)
Apart from feeling shame over my flawed code, I'd like to know some underlying facts, if possible.
The complexity of equal_range is constant with respect to the key I'm finding, in other words, linear wrt the number of matched keys. Is this always the average case regardless of the number of elements in the container?
In Boost 1.55 and prior, the complexity of equal_range(k) is linear wrt matched keys. Starting with Boost 1.56, the complexity is in fact constant, i.e, the same regardless of the number of matched keys, you can check this at: http://www.boost.org/doc/libs/1_56_0_b1/ libs/multi_index/doc/reference/hash_indices.html#lookup (All of this assuming a reasonably well-behaved hashing function.)
If I need to find, for instance, all items matching MSB, and within them, all matching HSB, will I need an additional key (composite key, if I understand)? It seems quite expensive from the memory point of view, so in case I have may items I could just iterate over them.
You can do it in either of two ways: * By having a composite key * By retrieving on MSB and linearly scanning for HSB matches. As it happens, I recently answered a question on this very topic: http://stackoverflow.com/a/24872210/213114
Instead of copying matched items, is it possible to have a "sub-container" akin to a view, without copying anything? Probably not...
Not sure what you mean... Could you elaborate? If your view happens to be the result of some equal_range query, then you could use the resulting iterator pair as a range for most postprocessing purposes. Joaquín M López Muñoz Telefónica
Joaquin M Lopez Munoz
Sensei
writes: My code, actually, won't find anything with equal_range, and find will return 0, although my call works in some sense, or better, the element >
I am passing DOES exist in the container. Source and output in the
following.
OK, some weird things are happening here, though I can only guess as the code you're showing is not complete. Seemingly p.equal_range(v) is returning an empty result (since the output does not emit any EQID). As for p.find(v) returning something, what's actually hapenning is that it is returning an iterator to end --this is why FIND outputs 0|0|0 when there's no such element in the container, judging from the ALL dump. This you can easily check by comparing u with p.end().
So... my wild guess is that Data::MSB is not working properly. Could you please show the code for that function? (even better, could you post a complete compilable snippet for me to play with?)
Ok, never mind, the problem is this: lookup functions take *keys*, not whole values, so instead of auto q = p.equal_range(v); you have to write auto q = p.equal_range(MSB(v)); The bug goes undetected at compile time because there's a default conversion from __uint128_t to uint64_t, but this conversion of course does not extract the MSB part, hence the run-time problems. In more usual scenarios where the key is a member of the element, the error would have been more apparent and the code wouldn't have compiled. Joaquín M López Muñoz Telefónica
On 7/31/14, 11:49am, Joaquin M Lopez Munoz wrote:
Ok, never mind, the problem is this: lookup functions take *keys*, not whole values, so instead of
auto q = p.equal_range(v);
you have to write
auto q = p.equal_range(MSB(v));
The bug goes undetected at compile time because there's a default conversion from __uint128_t to uint64_t, but this conversion of course does not extract the MSB part, hence the run-time problems. In more usual scenarios where the key is a member of the element, the error would have been more apparent and the code wouldn't have compiled.
Thanks Joaquin, you're very clear and helpful! Cheers!
participants (2)
-
Joaquin M Lopez Munoz
-
Sensei