C++ map comparsion - c++

Good morning, I'm stuck using a map in the correct way.
Situation
A database table with unique ID and two other codes
ID (long) | Type (long) | Name (string)
to fill the map correctly I've defined it in this way:
map<long, MyObject>
where key is my ID and the object holds all the stuff. The map works correctly, I load all rows and I navigate easily inside of it.
Troubles
Troubles come when I need to sort the rows using a criteria which is not the key but:
Type
Name
Looking around the Internet I found that I should:
Define the operator< for MyObject or...
Define another type of comparator for my map.
I did the step 1., but with no success (it is never called). I'm trying to do the point 2. but with even less success.
I'll paste some code to help:
class CSitoWebRigaVariante
{
public:
bool m_bSriDelete;
bool m_bSriVisibile;
long m_lSriId;
long m_lSriIdTipol;
long m_lSriCodGes;
CString m_strSriCodMat;
public:
CSitoWebRigaVariante(void);
CSitoWebRigaVariante(const CSitoWebRigaVariante& cRiga);
~CSitoWebRigaVariante(void);
bool operator<(const CSitoWebRigaVariante& cRiga);
void operator=(const CSitoWebRigaVariante& cRiga);
void Azzera(void);
static void CaricaDaMDB(CDB* pDB, long lIdVM, map<long, CSitoWebRigaVariante>& cRighe);
};
typedef map<long, CSitoWebRigaVariante> CSWRighe;
///> Static method to fill a map.
void CSitoWebRigaVariante::CaricaDaMDB(CADODatabase* pDB, long lIdVM, map<long, CSitoWebRigaVariante>& cRighe)
{
BOOL bValRit;
CRecordset* pRS;
CSitoWebRigaVariante riga;
CString strInt;
pRS = new CADORecordset(pDB);
strInt.Format(_T("SELECT * FROM SITOWEB_RIVARMAT WHERE sri_idvarmat = %ld;"), lIdVM);
cRighe.clear();
if (pRS->Open(strInt, CADORecordset::openQuery) == TRUE && pRS->GetRecordCount() > 0)
{
while (pRS->IsEOF() == FALSE)
{
bValRit = pRS->GetFieldValue(_T("sri_id"), riga.m_lSriId);
bValRit &= pRS->GetFieldValue(_T("sri_idtipol"), riga.m_lSriIdTipol);
bValRit &= pRS->GetFieldValue(_T("sri_codges"), riga.m_lSriCodGes);
bValRit &= pRS->GetFieldValue(_T("sri_codmat"), riga.m_strSriCodMat);
bValRit &= pRS->GetFieldValue(_T("sri_delete"), riga.m_bSriDelete);
bValRit &= pRS->GetFieldValue(_T("sri_visibile"), riga.m_bSriVisibile);
cRighe.insert(pair<long, CSitoWebRigaVariante>(riga.m_lSriCodGes, riga));
pRS->MoveNext();
}
}
pRS->Close();
delete pRS;
}
I'm using Visual Studio 2010, MFC.
Any help is appreciated.

std::map is not a multi-index associative container. Its find method (and other things) uses the key as a search criteria. There's no possibility to specify another search criteria. It's why it's a "single-index lookup table".
You can use Boost.MultiIndex. It was designed for your case and supports multiple indexes (as the name suggests), both unique and not-unique.
Or you can use multiple map instances with different keys. If keys are not unique you need std::multimap.

I would recommend you to use map for model (for storing data). When you need to display information, you can just output it in the order you need it to be shown. Sorting must be done not at level of storing items, but at level of displaying them.
Although, in each situation you will need to do reordering only once.
Also, if any help is appreciated, I would strongly recommend you to do a
typedef long MyId;
and to use VS 2015.

The map class provides a Compare parameter of the constructor. You could not complete your target by setting Compare as map has only support key compare function.
The first idea of mine is construct a class support your tables schema.
class Example
{
public:
<your code>
void sortByID();
void sortByType();
void sortByName();
private:
long ID_;
long Type_;
string Name_;
};
But it sounds terrible. As once your table change, you should hard copy.So why you just get result using database order by?

Related

Generate a unique ID C++

I want to generate a unique identifier "ident" for my complex structure, how can i do that?
in my header, the complexe structure is:
struct Complexe {
float x;
float y;
static unsigned int ident;
};
void Init(Complexe&);
etc...
and in the cpp file, i need to attribute ident a unique int
Init(Complexe&z){
z.ident = 0;
z.y = 0;
z.x = 0;
};
May I recommend you std::hash?
std::size_t ident = std::hash<Complex>()(complexVar);
Writing it from memory but it should return you unique value (with very small chance of it being not) for each Complex type object.
Consider UUID, specifically uuid_generate on GNU/Linux, or (it seems) UuidCreate on Windows.
Generating a unique id is easy even if you want to write your own algorithm. Though your algorithm will need some thoughts if the environment you are working in is multi-threaded. In this case you will need to write a thread safe code. for example below code will generate a unique id and is also thread safe:
class Utility {
public :
static int getUniqueId();
};
int Utility::getUniqueId() {
static std::atomic<std::uint32_t> uid { 0 };
return ++uid;
}
One simple way is to just make a free-list to help you reuse IDs. So for instance you start at ID 0 and whenever you create a structure, you first check the free-list for any released IDs. It will be empty the first time through so you increment the ID counter to 1 and use that for your new structure. When you destroy a structure it's ID goes back on the free-list (which can be implemented as a stack), so that the next time you create a structure that ID will be reused. If the free-list runs out of IDs you just start incrementing the ID counter from where you left off....wash, rinse and repeat. The nice thing about this method is that you will never wrap integer range and accidentally use an already in use ID if your program ends up running a long time. The main down side is the extra storage for the stack, but the stack can always grow as you need it.

Concurrent_hash_map implementation throwing SIGSEGV

I am trying to use tbb’s concurrent_hash_map to increase my application’s concurrent performance. Read up about it and implemented it according to my application but I am seeing crashes..
So, my application is a multi-threadedd application where I am storing pairs,the key is char* and the value is an integer. Pseudocode looks like this:
In .h file,
typedef tbb::concurrent_hash_map<const void*, unsigned, Hasher> tbb_concurrent_hash;
tbb_concurrent_hash concurrent_hash_table;
tbb_concurrent_hash::accessor write_lock;
tbb_concurrent_hash::const_accessor read_lock;
In .c file,
void storeName(const char * name) {
int id=0;
// This creates a pair object of name and index
std::pair object(name, 0);
// read_lock is a const accessor for reading. This find function searches for char* in the table and if not found, create a write_lock.
bool found = concurrent_hash_table.find(read_lock, name);
if (found == FALSE) {
concurrent_hash_table.insert(write_lock, name);
// Get integer id somehow.
id = somefunction();
write_lock->second = id;
write_lock.release();
} else {
// if the name is found in the table then get the value and release it later
id = read_lock->second;
read_lock.release();
}
}
As per my understanding, I am good with the implementation but as I said, there are multiple crashes coming when find returns me FALSE. Crash have traces of mutexs as well.
Your 'locks', i.e. accessors are declared global in .h file. so, basically you write to a shared scoped_lock instance... which logically leads to a data race. Accessors are like fused std::shared_ptr and std::scoped_lock classes in one, or simpler - a pointer for your result and a lock guard for the data it points. You don't want to use one global pointer from multiple threads. Declare them locally in a scope you want to have that access (and you'd not need to call .release() as well)
Another problem is the data race between find() and insert (). Two or more threads can decide that they have to insert since they found nothing. In this case, the first thread will insert the new element while other threads will return existing element because insert() acts as find() if there's existing element. The problem is that your code doesn't account for that.
I can see why you might want to double check using const_accessor as the read lock is more scalable. But instead, you might want to use bool insert( const_accessor& result, const value_type& value ); with read lock (const_accessor) and value_type instead of a key only, which will initialize the whole pair in the case when a new element is added.

BerkeleyDB Db->get not working when using custom comparison function

In a C++ program I am trying to set a custom comparison function for a Berkeley DB, using the Db::set_bt_function member function (the DB is opened as a BTREE type). My code works fine when I'm not changing the comparison function; I can put and get keys/values using Db::put and Db::get.
To try the set_bt_function method, I defined my own "lexicographic comparison" as follows:
int compkeys(Db *db, const Dbt *dbt1, const Dbt *dbt2, size_t *locp) {
size_t s = dbt1->get_size() > dbt2->get_size() ? dbt2->get_size() : dbt1->get_size();
int c = std::memcmp(dbt1->get_data(), dbt2->get_data(), s);
if(c != 0) return c;
if(dbt1->get_size() < dbt2->get_size()) return -1;
if(dbt1->get_size() > dbt2->get_size()) return 1;
return 0;
}
So this should lead to exactly the same behavior as my reference code, when the comparison function isn't changed, since by default Berkeley DB uses lexicographical order.
Yet, when using this comparison function, Db::get doesn't work anymore. It returns -30999 (DB_BUFFER_SMALL).
Here is what I am doing to get the value associated with a given key:
Db* _dbm = ... /* DB is open */
std::vector<char> mykey;
... /* mykey is set to some content */
Dbt db_key((void*)(mykey.data()), uint32_t(mykey.size()));
Dbt db_data;
db_key.set_flags(DB_DBT_USERMEM);
db_data.set_flags(DB_DBT_MALLOC);
int status = _dbm->get(NULL, &db_key, &db_data, 0);
... /* check status, do something with db_data */
free(db_data.get_data());
Any idea why this code works when I'm not setting the comparison function, and doesn't when I am?
Note: if I access key/values using a cursor (Dbc::get) I don't have this issue.
The DB_BUFFER_SMALL error in this case is complaining about your db_key Dbt. You need to call db_key.set_ulen(uint32_t(mykey.size())) to tell BDB how much space you've allocated to hold the keys that come out of the database.
Things get a little weirder when you're using a custom comparison function. You can have data in the key that's not part of the compare - and not in the key that you passed in to get(). For this reason, BDB returns the key it found in the database in your db_key.
When setting the ulen, make it large enough to hold any key that can come back from the database. You may find that it's saner to just keep a char array on the stack to deal with this key in/out behavior.

c++ stl selection for multiple keys

c++ stl experts,
In a protocol stack implementation, I have a Message being sent from one layer to another. The source layer stores some information, and processes that information, on recepton of response from the second layer.
Now the information stored has 3 parameters which is used to compare the responses from the destination layer. (to get the correct one). i.e lets says session id, request number and infoID. The stored info contains a struture, lets say struct A.
which is the best way to implement this in the source layer to store info ?
Initially i thought of the following, as then there were only two keys
std::map<std::pair<u32, u32>, StructA> m_mSessionId2RNum2StructA;
But later requirement for another key. this got complicated
struct StructZ
{
u32 InfoId;
StructA stStructA;
};
std::map<std::pair<u32, u32>, StructZ> m_mSessionId2RNum2StructZ;
This doesnot look good. Any inputs/suggestions to improve this much appreciated
thanks
~pdk
Perhaps StructK can be a key for StructA as a value in a map:
struct StructK
{
u32 k1;
u32 k2;
u32 k3;
};
inline bool operator< (const StructK& lhs, const StructK& rhs)
{
if(lhs.k1 < rhs.k1)
return true;
else
if(lhs.k1 == rhs.k1)
{
if(lhs.k2 < rhs.k2)
return true;
else
if(lhs.k2 == rhs.k2)
{
return lhs.k3 < rhs.k3;
}
else
return false;
}
else
return false;
}
and then
map<StructK, StructA> myMap;
Of course, you can use any logic for operator<
std::pair is a special type of std::tuple which can only hold two values, whereas tuple can hold dozens (1). I.e. you seem to want std::tuple<u32, u32,u32> as a key now.
You get operator< for free with tuple, same as with pair.
If you need alternate indices, i.e. search by key 1, 2 or 3, then you need boost::multi_index containers.
(1) Check your implementation for precise limits.

Sorting wxListCtrl alphabetically without using SetItemData

I've implemented a wxListCtrl and would like to sort the list alphabetically. Although this sounds similar as the question here, it is different with one major difference: I'm using SetItemData() and GetItemData() to store the index of a vector.
The code supplied here works nicely, but requires GetItemData(). Even though the comparison function does not refer to any of the item data, if I omit it, the sorting is not complete.
static int wxCALLBACK MyCompareFunction(long item1, long item2, long sortData) {
wxSortedListCtrl *ctrl = (wxSortedListCtrl*) sortData;
wxString a, b;
a = ctrl->GetItemText(item1, ctrl->GetSortedColumn());
b = ctrl->GetItemText(item2, ctrl->GetSortedColumn());
if( ctrl->IsSortedAscending() )
return a.CmpNoCase(b);
return b.CmpNoCase(a);
}
bool wxSortedListCtrl::SortItems(void) {
long item = -1;
for ( ;; ) {
item = GetNextItem(item);
if ( item == -1 )
break;
SetItemData(item, item);
//this is needed even though MyCompareFunction doesn't use it AT ALL.
//however it overwrites the data that I use myself...
}
return wxListCtrl::SortItems(MyCompareFunction, (long)this);
}
How would I use this sort function while maintaining the item data I've set myself with SetItemData()?
If you want to rely on the control to do the sorting (as opposed to resorting the items internally and just reinserting them into it in the correct order), you must be able to access the sort key (i.e. text in your case) via the item data. So, instead of just associating your own data with each item, associate some struct containing both the item text and your custom data with it.
Alternatively, use wxLC_VIRTUAL and just override OnGetItemXXX() to return the items in the correct order. This may sometimes be simpler than dealing with sorting the items manually and is definitely much more efficient for any non-trivial number of items.