Finding Key in std::unordered_map with custom key - c++

I'm currently creating a custom std::unordered_map declaration with my custom key:
class BASE_DLLSPEC ClientKey
{
private:
// this is always true initially until we call SetClientId
bool emptyId;
// both of these are guaranteed to be unique
QString m_connectId; // ip:port format
QString m_clientId; // {Uuid} format
// ----------
public:
ClientKey(const QString& connectId = "", const QString& clientId = "") :
emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }
void SetClientId(const QString& clientId)
{
m_clientId = clientId;
emptyId = false;
}
const QString& GetConnectId() const { return m_connectId; }
const QString& GetClientId() const { return m_clientId; }
bool operator==(const ClientKey& other) const
{
int comp1 = QString::compare(m_connectId, other.GetConnectId());
int comp2 = QString::compare(m_clientId, other.GetClientId());
return (comp1 == 0) ||
(!emptyId && comp2 == 0);
}
};
struct BASE_DLLSPEC ClientKeyHash
{
std::size_t operator()(const ClientKey& key) const
{
std::string connectId = key.GetConnectId().toStdString();
std::string clientId = key.GetClientId().toStdString();
std::size_t h1 = std::hash<std::string>()(connectId);
std::size_t h2 = std::hash<std::string>()(clientId);
return h1 ^ (h2 << 1);
}
};
struct BASE_DLLSPEC ClientKeyEqual
{
bool operator()(const ClientKey& lhs, const ClientKey& rhs) const
{
return lhs == rhs;
}
};
typedef std::unordered_map<ClientKey,
ClientPtr,
ClientKeyHash,
ClientKeyEqual> ClientMap;
I'm having difficulties finding a particular key during my iteration. For some reason my client object is never located when I pass in a key for lookup.
ClientKey key = Manager::ClientKey(connectId);
ClientManager& clientManager = Manager::ClientManager::GetInstance();
ClientMap::const_iterator clientIter = clientManager.GetClients().find(key);
Even if the key has already been inserted, clientIter is always pointing to the end iterator position. Do you think this is related to having to re-create these ClientKey values on the stack and then passing them into the map for look-up, or do I have a problem elsewhere? Thank you for the clarification and insight.

At first, some considerations to the emptyId field (do not consider invalid formats - which, by the way, is not checked by you either):
ClientKey k0("hello", "world");
ClientKey k1("hello");
k1.SetClientId("world");
Is there any particular reason that the emtpyId flag should be different for k0 and k1? I personally would say:
The flag is implemented incorrectly.
It is redundant, you get the same information via m_clientId.empty().
Now the reason for failure:
Consider again k0 and k1, but without SetClientId having been called on k1:
ClientKey k0("hello", "world");
ClientKey k1("hello");
Imagine k0 has been inserted in the map, and with k1 you try to find it. What will happen? k1 produces another hash key than k0, and the map will look at a different bucket than where k0 resides at - and will not find anything.
What I think you want to achieve is having several clients for the same connection id and being able to iterate over these for a given connection id. So you might prefer std::unordered_multimap<std::string, ClientPtr> (where the string parameter represents the connection id). You will get all clients for a given connection id via equal_range then, and your class ClientKey gets obsolete.

Your code allows that the following will return true:
ClientKey k1("hello", "world");
ClientKey k2("hello", "");
return k1 == k2;
However, your hash is based on the combination of connectId and clientId.
unordered_map::find does not do an exhaustive search of the map, instead it looks in the bucket for the given hash and compares just the entries in the bucket.
You are generating your test key with just connectId, so it is looking in the bucket for ClientKey(connectId, "") rather than the bucket for ClientKey(connectId, someOtherValue).
You should consider making the hash based exclusively on connectId.
Lastly, note your constructor:
ClientKey(const QString& connectId = "", const QString& clientId = "") :
emptyId(true), m_connectId(connectId), m_clientId(clientId)
{ }
If I write:
ClientKey ck("hello");
should emptyId really be true?

Related

How to use shared_pointer as value in a std::map structure?

I have a map structure as below which has shared_pointer of a protobuf structure Stats:
map<string, shared_pointer<vol::proto::Stats> statsMap;
I am storing and retrieving the map entries through a LRU cache implementation
template class LRUCache<string,std::shared_ptr<vol::proto::Stats>>;
Protobuf structure:-
message Stats {
required string oid = 1;
required int64 logical_size = 2;
required int64 committed_size = 3;
}
message StatsRequest {
required string uuid = 1;
}
The function for storing entry into the map:-
template<class K, class V>
void
LRUCache<K, V>::Put(const K& key, V value)
{
Vmacore::System::WriteSynchronized lock(this);
auto val = refMap.find(key);
if (val == refMap.end()) {
if (cacheSize == refMap.size()) {
K leastUsedKey = lruList.back();
lruList.pop_back();
refMap.erase(leastUsedKey);
}
} else {
lruList.erase(val->second.lruKeyRef);
}
lruList.push_front(key);
refMap[key] = LRUValueReference<K, V>(lruList.begin(), value);
}
void PutS(const string& Id, const vol::proto::Stats& info)
{
shared_ptr<vol::proto::Stats> statsInfo = make_shared<vol::proto::Stats>(info);
_StatsCache.Put(Id, statsInfo);
}
void PutStats(vol::proto::StatsRequest &req) {
vol::proto::Stats *stats;
GetStats(stats); //stats gets populated here.
PutS(stats->oid(), *stats);
}
To Get the entry from the map:-
template<class K, class V>
bool
LRUCache<K, V>::Get(const K& key, V& value)
{
Vmacore::System::WriteSynchronized lock(this);
auto val = refMap.find(key);
if (val == refMap.end()) {
return false;
}
lruList.splice(lruList.begin(), lruList, val->second.lruKeyRef);
val->second.lruKeyRef = lruList.begin();
value = val->second.value;
return true;
}
void GetS(const string& Id, shared_ptr<vol::proto::Stats> info)
{
if (!_StatsCache.Get(Id, info))
return false;
return true;
}
void GetStats(vol::proto::StatsRequest &req) {
shared_ptr<vol::proto::Stats> stats;
if (GetS(req.uuid(), stats)) {
Log("object id is %s", stats.get()->oid()); // Crash at oid (this=0x0)
}
}
Now while doing GetStats, I am seeing a crash as to in GetStats's Log statement that is program terminated with signal SIGSEGV, Segmentation fault. Should I do a make_share for Stats in GetStats? Can you please describe as to what has caused this crash and how to fix the crash here?
You're passing a raw pointer from GetStats to GetS, which then wraps it into a shared_ptr. Now lets start ref-counting.
GetS -> increase on entry > 1
_StatsCache.Get -> pass by ref > 1
GetS -> decrease on exit > 0
Ref count 0 -> delete the pointer.
In GetStats you then try to de-reference the stats pointer by calling .get, but it was just deleted.
You should either always use smart pointers, or be very very very careful when you convert from raw pointer to smart pointer, and back.
Actually what I wrote is wrong. What you're doing is actually much worse.
You're wrapping a raw pointer in GetS into a smart pointer, and then pass it by ref to your _StatsCache.Get, which then assigns a different shared_ptr to yours. So you've overwritten the original raw pointer with something else.
But when GetS returns, that shared_ptr is now lost and destructed, but the original stats pointer is accessed in stats.get(). What's in it? Nothing from GetS, that's for sure.

Is it neccessary to use Json::value object=Json::objectValue to init?

To work with json using a library jsoncpp,is it neccessary to use Json::objectvalue?
Json::Value jsn_req;
jsn_req["header"] = Json::objectValue;
jsn_req["header"]["sid"] = 0;
or:
Json::Value jsn_req;
jsn_req["header"]["sid"] = 0;
What's the difference between this?
read the source code, may help. operator[] create null value if the key is not exist. Then a null value can add not exist key too.
diff:
the first just trans the value of "header" from null to object, the second doesn't do it.
the first find "header" twice, but the second only find it once.
Both of then could work, but the second must be quick, it's not necessary to trans it.
Value& Value::operator[](const char* key) {
return resolveReference(key, key + strlen(key));
}
Value& Value::resolveReference(char const* key, char const* end) {
JSON_ASSERT_MESSAGE(
type() == nullValue || type() == objectValue,
"in Json::Value::resolveReference(key, end): requires objectValue");
if (type() == nullValue)
*this = Value(objectValue);
CZString actualKey(key, static_cast<unsigned>(end - key),
CZString::duplicateOnCopy);
auto it = value_.map_->lower_bound(actualKey);
if (it != value_.map_->end() && (*it).first == actualKey)
return (*it).second;
ObjectValues::value_type defaultValue(actualKey, nullSingleton());
it = value_.map_->insert(it, defaultValue);
Value& value = (*it).second;
return value;
}

Call different processing functions for attributes in an XML element

When handling XML attributes in C++, how should different operations be run for different attributes?
Currently, I have something like this:
// get list of attributes for an XML element into member called 'attributes'
// ...
// run appropriate functions for each attribute
for (auto attribute : attributes)
{
auto name = attribute.name;
auto value = attribute.value;
if (name == "x")
doSomethingWithX(value);
else if (name == "y")
doSomethingWithY(value);
}
For just a few attribute names, this isn't so bad - but with a larger number (>15) this starts to look messy and I'm concerned about performance issues.
What might be a better way of handling XML attributes like this?
You can use a std::unordererd_map<std::string, std::function<void (const std::string&)>> and set it up with appropriate lambda functions:
std::unordererd_map<std::string, std::function<void (const std::string&)>> attrProcessors = {
{ "X", [](const std::string& value) {
// Do something with value
} } } ,
{ "Y", [](const std::string& value) {
// Do something with value
} } }
};
// run appropriate functions for each attribute
for (auto attribute : attributes)
{
auto name = attribute.name;
auto value = attribute.value;
auto processorEntry = attrProcessors.find(name);
if(processorEntry != attrProcessors.end()) {
(*processorEntry).second(value);
}
}
I am not so sure though that maintenace of the map entries would be easier to read than the if / else if cascade.
On the other hand you won't need to create an extra function for each attribute name.

Need an example using dht_put_item with libtorrent

I'm trying to use dht to keep mutable data with libtorrent. As far as I can understand, the right way is to use the method dht_put_item from session. The problem is that I need to pass a callback function and I don't know what I'm doing wrong... my code looks like the following
namespace lt = libtorrent;
//The callback function
void cb(lt::entry& cdentry, boost::array<char,64>& cbarray, boost::uint64_t& cbint, std::string const& cbstring){
//My stuff here
}
void main(){
//The session
lt::session ses;
//The data I want to insert into DHT
std::string cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
//Now I create the keys
unsigned char seed[32];
unsigned char public_key[32];
unsigned char private_key[64];
unsigned char signature[32];
ed25519_create_seed(seed);
ed25519_create_keypair(public_key, private_key, seed);
ed25519_sign(signature, cadenaStr.c_str(), sizeof(cadenaStr.c_str()), public_key, private_key);
//How can I use this?, where is the data supposed to go? :|
ses.dht_put_item(public_key, cb, false);
}
On libtorrent/session_handler.hpp this method is defined as
void dht_put_item(boost::array<char, 32> key
, boost::function<void(entry&, boost::array<char,64>&
, boost::uint64_t&, std::string const&)> cb
, std::string salt = std::string());
Can someone please tell me what I'm doing wrong.
Thanks!
There is an example in the libtorrent repository that I use for testing. It can generate keys, put and get both mutable and immutable items.
https://github.com/arvidn/libtorrent/blob/master/tools/dht_put.cpp
How can I use this?, where is the data supposed to go? :|
You provide the data in the callback that's called. The reason for this kind of API is that there are use cases where you want to mutate the data, and then you need to first know whether something is already stored under this key, and what it is.
You are missing the settings pack for your session.
lt::settings_pack settings;
settings.set_bool(settings_pack::enable_dht, false);
settings.set_int(settings_pack::alert_mask, 0xffffffff);
ses.apply_settings(settings);
settings.set_bool(settings_pack::enable_dht, true);
ses.apply_settings(settings);
Then you need to wait until you receive a boostrap message by waiting for an alert.
wait_for_alert(ses, dht_bootstrap_alert::alert_type);
Last, your dht_put_item call:
char const* cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
dht_put_item(public_key, std::bind(&put_string, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, public_key, private_key, cadenaStr));
You will need these functions:
static alert* wait_for_alert(session* ses, int alert_type)
{
alert* ret = nullptr;
bool found = false;
while (!found)
{
ses->wait_for_alert(seconds(5));
std::vector<alert*> alerts;
ses->pop_alerts(&alerts);
for (std::vector<alert*>::iterator i = alerts.begin()
, end(alerts.end()); i != end; ++i)
{
if ((*i)->type() != alert_type)
{
continue;
}
ret = *i;
found = true;
}
}
return ret;
}
static void put_string(
entry& e
,boost::array<char, 64>& sig
,boost::int64_t& seq
,std::string const& salt
,boost::array<char, 32> const& pk
,boost::array<char, 64> const& sk
,char const* str)
{
using dht::sign_mutable_item;
if (str != NULL) {
e = std::string(str);
std::vector<char> buf;
bencode(std::back_inserter(buf), e);
dht::signature sign;
seq++;
sign = sign_mutable_item(buf, salt, dht::sequence_number(seq)
, dht::public_key(pk.data())
, dht::secret_key(sk.data()));
sig = sign.bytes;
}
}

Get the "name" of a key from QKeyEvent in Qt

Is there an easy way of getting the name of a key (so something like "uparrow" from a key event instead of just getting the key code "16777235")? Do I have to make a list of key names myself?
Using human-readable names in your code
You can use the Qt::Key enum, or get the key as a string with QKeyEvent::text().
From QKeyEvent documentation:
int QKeyEvent::key () const
Returns the code of the key that was pressed or released.
See Qt::Key for the list of keyboard codes. These codes are independent of the underlying window system. Note that this function does not distinguish between capital and non-capital letters, use the text() function (returning the Unicode text the key generated) for this purpose.
...
Qt::Key is an enum that maps numeric key IDs (like the return value of QKeyEvent::key()) to programmer-readable names like Qt::Key_Up.
If you only care about alphanumeric keys, you can also use QKeyEvent::text() to get the value:
QString QKeyEvent::text () const
Returns the Unicode text that this key generated. The text returned can be an empty string in cases where modifier keys, such as Shift, Control, Alt, and Meta, are being pressed or released. In such cases key() will contain a valid value.
See also Qt::WA_KeyCompression.
Displaying human-readable names to the user
Use QKeySequence::toString() or build your own table of "nice" names.
The easiest way to get human-readable key names to show to the user is to use QKeySequence::toString().
Here's an example:
Qt::Key key = Qt::Key_Up;
qDebug() << QKeySequence(key).toString(); // prints "Up"
If you don't like the names that QKeySequence uses (e.g. you want to use "Up Arrow" instead of "Up"), you'll need to make your data table to remap the enum values to your preferred names.
Another approach leverages the Qt metaobject system and the introspection into most enumerations in the Qt namespace. This works in both Qt 4 and Qt 5.
// https://github.com/KubaO/stackoverflown/tree/master/questions/keyname-21764138
#include <QMetaEnum>
namespace SO {
enum KeyNameOption { KeyNameNone = 0, AppendArrow = 1 };
Q_DECLARE_FLAGS(KeyNameOptions, KeyNameOption)
}
QString keyName(int index, SO::KeyNameOptions opt = {}) {
constexpr static auto const getEnum = [](const char *name) {
int enumIndex = qt_getQtMetaObject()->indexOfEnumerator(name);
return qt_getQtMetaObject()->enumerator(enumIndex);
};
static const auto keyEnum = getEnum("Key");
static const auto modifierEnum = getEnum("KeyboardModifiers");
auto name = modifierEnum.valueToKeys(index & Qt::KeyboardModifierMask);
index &= ~Qt::KeyboardModifierMask;
if (name == "NoModifier")
name.clear();
else {
name.replace('|', '+');
name.replace("Modifier", "");
name.append('+');
}
auto keyName = keyEnum.valueToKey(index);
if (keyName)
name.append(keyName + 4);
if ((opt & SO::AppendArrow) && index >= Qt::Key_Left && index <= Qt::Key_Down)
name.append(" Arrow");
return QLatin1String(name);
}
int main() {
Q_ASSERT(keyName(Qt::Key_Tab) == "Tab");
Q_ASSERT(keyName(Qt::ShiftModifier | Qt::Key_Up, SO::AppendArrow) == "Shift+Up Arrow");
Q_ASSERT(keyName(Qt::AltModifier | Qt::Key_Down) == "Alt+Down");
}
You'd then use it in, say, keyPressEvent, as follows:
void MyWidget::keyPressEvent(QKeyEvent * ev) {
qDebug() << keyName(ev->key());
}