6

Is this an appropriate way to provide unique keys in a map? In other words, is the key being made from the unique value contained in the uuid, or is it being made from the pointer to the uuid_t struct? A side question, is there a more efficient container, when I don't care about the ordering by keys inside the container?

#include <uuid/uuid.h>

int main(int argc, char **argv)
{    
   std::map<uuid_t,int> myMap;         

   uuid_t id1;
   uuid_t id2;

   uuid_generate( (unsigned char *)&id1 );  
   uuid_generate( (unsigned char *)&id2 );

   myMap[id1] = 5;
   myMap[id2] = 4;

}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Dave
  • 2,653
  • 4
  • 20
  • 22
  • Why do you need the keys then? Give us more "background". Why not `vector` or simple `array`. There are many types of containers. Is there a reason you need the unique id? – Kiril Kirov Nov 20 '11 at 09:24
  • 1
    Thats a very good question Kiril, and it got me thinking about the issue. I am building an application which may potentially need to aggregate data created by different, disconnected users, running different instances of an app -- with no shared db to talk to. Thus keying with an auto-incrementing integer wouldn't work -- the idea being to not generate any key collisions across all incoming data sources. I suppose some combo of a machine_id, a timestamp and a user would work too, but I don't really understand how i'd use hashing to generate a unique key. – Dave Nov 20 '11 at 10:04
  • I'm looking for a high-performance index, so trying to avoid using strings due to what I'm guessing would be a LOT of string compares. Does this help? – Dave Nov 20 '11 at 10:05
  • I'm not sure I understood all of this. Do you mean, that these users will be identified somehow and that the same user may be connected several times and you need to store all user's data on the same place? – Kiril Kirov Nov 20 '11 at 10:12
  • Not quite, but close. The different users will be generating data, potentially at the same time, with no connection to each other. Later, I need to merge the records, and to facilitate linking between them, somehow be able to point one record to another. I think a link will consist of a description and the unique identifier (whatever it turns out to be) of the target record. It is part of a collective knowledge management application. – Dave Nov 20 '11 at 10:21
  • The hypothetical index I pseudo-coded above, is used to quickly access the linked records, so that the search implementation can quickly scan records that have been linked to each other. In actuality, the int being stored as the values in the map is just a simplification to keep the question focused, I'm actually storing pointers to cached records... I just didn't think that part was relevant! :) – Dave Nov 20 '11 at 10:24
  • Ah, I see now. Sorry, I can't really help here. Good luck, anyway :) – Kiril Kirov Nov 20 '11 at 10:35
  • `void uuid_copy( uuid_t dst , uuid_t src);` should attract your attention here as well as `void uuid_generate( uuid_t out );`. They should rise the question: How does those function fill `dst` and `out` if there is no neither `ptr` nor `reference` modification of `uuid_t`? – ony Nov 20 '11 at 13:44
  • btw, you may want to look at [`std::hash_map`](http://www.sgi.com/tech/stl/hash_map.html) – ony Nov 20 '11 at 17:24
  • @ony Because uuid_t in libuuid is defined as an array type, and array types in parameter declarations are silently converted into pointer types. – rodrigo Nov 21 '11 at 09:45
  • @rodrigo, that's exactly what I was pointing at ;) actually they are not converted (just put `uuid_t` inside `struct` and everything will change), but when `uuid_t` passed directly (as return value or argument) it is treated as a pointer – ony Nov 21 '11 at 21:10
  • @ony, you raise a good point, that I hadn't included in my question... order does NOT matter in my implementation. I should probably look at std::hash_map, and std::unordered_map instead of std::map, and see which of those is more appropriate (I currently don't know those well enough to differentiate). – Dave Mar 08 '15 at 21:30
  • @Dave, as far as I know there should be no difference between `std::hash_map` and `std::unordered_map`. Last one is from C++11, other one is pre-C++11. The only diff is in C++11 features (movable, emplace, etc) – ony Mar 09 '15 at 07:41

3 Answers3

5

I guess the best way of using third-party C-structures is to use them through their friendly functions. So if you wanna use uuid_t in STL, I'd suggest you to create some kind of C++ interface/wrapper to that structure like

struct Uuid {
  uuid_t uuid;
  Uuid(const Uuid &other) { uuid_copy(uuid, other.uuid); }
  Uuid(const uuid_t other_uuid) { uuid_copy(uuid, other_uuid); }
  void generateInplace() { uuid_generate(uuid); }
  static Uuid generate() { Uuid wrapped; uuid_generate(wrapped.uuid); return wrapped; }
  bool operator<(const Uuid &other) { return uuid_compare(uuid, other.uuid) < 0; }
  bool operator==(const Uuid &other) { return uuid_compare(uuid, other.uuid) == 0; }
  // ...
};

That should hide from you the fact that uuid_t isn't structure but pointer to array (i.e. typedef unsigned char uuid_t[16]).

Note: there is boost version of uuid library

ony
  • 12,457
  • 1
  • 33
  • 41
  • This doesn't answer how you generate a unique hash value from a `uuid_t` though. – Mark Ingram Nov 13 '13 at 13:47
  • @MarkIngram, `std::map` doesn't use hash values. It uses only comparison operator. If you need to use this class in `std::unordered_map` (hash-table) you have to define either `std::hash` and `std::equal_to` or need to override appropriate template parameters. But this would be a different question and different answer. – ony Nov 13 '13 at 14:29
  • Apologies, I was looking for unordered_map myself and didn't spot that OP only specified map. – Mark Ingram Nov 13 '13 at 16:51
  • @ony, Thanks for the additional info for std::hash, that very well may be the ideal solution! – Dave Mar 08 '15 at 21:31
4

The STL containers always contain copies of the object, and that applies to map keys, also.

The simplest way to support this is using a custom comparator for the map.

struct UUIDComparator
{
    bool operator()(const uuid_t &a, const uuid_t &b)
    {
        //compare and return a < b
    }
};
std::map<uuid_t, int, UUIDComparator> map;

Another slightly controversial solution would be to convert the uuid_t into a std::pair<uint64_t, uint64_t> as both types are 128 bits wide, and, AFAICT, layout compatible. And the std::pair are directly usable as map keys.

std::map<std::pair<uint64_t, uint64_t>, int, UUIDComparator> map;
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • As I mentioned before `uuid_t` is actually type `unsigned char[16]`. Wouldn't it mean that `std::map::key_type` will have type `usigned char*` and all operations like `operator[](...)` will work with references to pointers? – ony Nov 20 '11 at 14:56
  • Oh, I thought you said __uuid_t struct__. If it is an arrany type, it will not work, because array types are not copiable. And not, it will not decay into a pointer, that is for expressions, not typedefs. But if it is a struct, even with an array within, it will work just fine. – rodrigo Nov 20 '11 at 15:12
  • That's not mine question, but since there is `#include ` I assume that `uuid_t` is `typedef` from libuuid library. At my system that library defines this type as array. About decaying to the pointer - you are right, but constructor of `std::pair` will not accept `uuid_t` as first argument, I guess. – ony Nov 21 '11 at 06:49
2

Simpler: uuid_unparse(...) converts it into a char* (37 chars long), which you can then wrap a string around...

Don Doerner
  • 71
  • 1
  • 4