-
-
Notifications
You must be signed in to change notification settings - Fork 345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce ObjectMap #1638
Introduce ObjectMap #1638
Changes from 7 commits
57b1809
8c71732
41fbde9
38fcc23
d0c8030
e3eb59c
904b8ba
79269c0
23686a6
02404b4
6efbaf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
/**** | ||
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. | ||
* Created 2015 by Skurydin Alexey | ||
* http://github.com/SmingHub/Sming | ||
* All files of the Sming Core are provided under the LGPL v3 license. | ||
* | ||
* ObjectMap.h | ||
* | ||
* @author: 31 Jul 2018 - Mikee47 <[email protected]> | ||
* | ||
*/ | ||
|
||
#ifndef _SMING_CORE_DATA_OBJECT_MAP_H_ | ||
#define _SMING_CORE_DATA_OBJECT_MAP_H_ | ||
|
||
#include "WVector.h" | ||
|
||
/** @brief Implementation of a HashMap for owned objects, i.e. anything created with new(). | ||
* @note Once added to the map the object is destroyed when no longer required. | ||
* | ||
* To free an object, use one of: | ||
* | ||
* ``` | ||
* map.remove(key); | ||
* map.removeAt(index); | ||
* map[key] = nullptr; // Free existing object and set to null | ||
* ``` | ||
*/ | ||
template <typename K, typename V> class ObjectMap | ||
{ | ||
public: | ||
ObjectMap() | ||
{ | ||
} | ||
|
||
~ObjectMap() | ||
{ | ||
clear(); | ||
} | ||
|
||
/* Allows operator[] to be used to safely set values */ | ||
class Value | ||
{ | ||
public: | ||
/* Functor to provide guarded access to values */ | ||
Value(V*& value) : value(value) | ||
{ | ||
} | ||
|
||
Value& operator=(V* newValue) | ||
{ | ||
delete value; | ||
value = newValue; | ||
return *this; | ||
} | ||
|
||
operator const V*() const | ||
{ | ||
return value; | ||
} | ||
|
||
operator V*() | ||
{ | ||
return value; | ||
} | ||
|
||
V* operator->() | ||
{ | ||
return value; | ||
} | ||
|
||
private: | ||
V*& value; | ||
}; | ||
|
||
/** | ||
* @brief Get the number of entries in this map | ||
* @retval int Entry count | ||
*/ | ||
unsigned count() const | ||
{ | ||
return entries.count(); | ||
} | ||
|
||
/* | ||
* @brief Get a key at a specified index, non-modifiable | ||
* @param idx the index to get the key at | ||
* @return The key at index idx | ||
*/ | ||
const K& keyAt(unsigned idx) const | ||
{ | ||
return entries[idx].key; | ||
} | ||
|
||
/* | ||
* @brief Get a key at a specified index | ||
* @param idx the index to get the key at | ||
* @return Reference to the key at index idx | ||
*/ | ||
K& keyAt(unsigned idx) | ||
{ | ||
return entries[idx].key; | ||
} | ||
|
||
/* | ||
* @brief Get a value at a specified index, non-modifiable | ||
* @param idx the index to get the value at | ||
* @retval The value at index idx | ||
* @note The caller must not use `delete` on the returned value | ||
*/ | ||
const V* valueAt(unsigned idx) const | ||
{ | ||
return entries[idx].value; | ||
} | ||
|
||
/* | ||
* @brief Get a value at a specified index | ||
* @param idx the index to get the value at | ||
* @retval The value at index idx | ||
* @note Because a reference is returned any existing value must be `delete`d first | ||
* @see `operator[]` | ||
*/ | ||
Value valueAt(unsigned idx) | ||
{ | ||
return entries[idx].value; | ||
} | ||
|
||
/** | ||
* @brief Get value for given key, if it exists | ||
* @param key | ||
* @retval const V* Will be null if not found in the map | ||
*/ | ||
const V* operator[](const K& key) const | ||
{ | ||
return find(key); | ||
} | ||
|
||
/** @brief Access map entry by reference | ||
* @param key | ||
* @retval Value Guarded access to mapped value corresponding to given key | ||
* @note If the given key does not exist in the map then it will be created and a null value entry returned. | ||
* | ||
* Example: | ||
* | ||
* ``` | ||
* void test() | ||
* { | ||
* ObjectMap<String, MyType> map; | ||
* MyType* object1 = new MyType(); | ||
* map["key1"] = object1; | ||
* auto value = map["key1"]; // value now refers to object1 | ||
* value = nullptr; // Free object1 | ||
* MyType* object2 = new MyType(); | ||
* value = object2; | ||
* // As soon as `map` goes out of scope, object2 is released | ||
* } | ||
* ``` | ||
* | ||
* @see `valueAt()` | ||
* | ||
*/ | ||
Value operator[](const K& key) | ||
{ | ||
int i = indexOf(key); | ||
if(i >= 0) { | ||
return entries[i].value; | ||
} | ||
|
||
auto entry = new Entry(key, nullptr); | ||
entries.addElement(entry); | ||
return entry->value; | ||
} | ||
|
||
/** @brief Set a key value | ||
* @param key | ||
* @param value | ||
*/ | ||
void set(const K& key, V* value) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That
The best would have been to make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Done :-) |
||
{ | ||
operator[](key) = value; | ||
} | ||
|
||
/** | ||
* @brief Find the value for a given key, if it exists | ||
* @param key | ||
* @retval V* Points to the object if it exists, otherwise nullptr | ||
* @note If you need to modify the existing map entry, use `operator[]` or `valueAt()` | ||
*/ | ||
V* find(const K& key) const | ||
{ | ||
int index = indexOf(key); | ||
return (index < 0) ? nullptr : entries[index].value; | ||
} | ||
|
||
/** | ||
* @brief Get the index of a key | ||
* @param key | ||
* @retval int The index of the key, or -1 if key does not exist | ||
*/ | ||
int indexOf(const K& key) const | ||
{ | ||
for(unsigned i = 0; i < entries.count(); i++) { | ||
if(entries[i].key == key) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
} | ||
|
||
/** | ||
* @brief Check if a key is contained within this map | ||
* @param key the key to check | ||
* @retval bool true if key exists | ||
*/ | ||
bool contains(const K& key) const | ||
{ | ||
return indexOf(key) >= 0; | ||
} | ||
|
||
/** | ||
* @brief Remove entry at given index | ||
* @param index location to remove from this map | ||
*/ | ||
void removeAt(unsigned index) | ||
{ | ||
entries.remove(index); | ||
} | ||
|
||
/** | ||
* @brief Remove a key from this map | ||
* @param key The key identifying the entry to remove | ||
*/ | ||
void remove(const K& key) | ||
{ | ||
int index = indexOf(key); | ||
if(index >= 0) { | ||
removeAt(index); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Clear the map of all entries | ||
*/ | ||
void clear() | ||
{ | ||
entries.clear(); | ||
} | ||
|
||
protected: | ||
struct Entry { | ||
K key; | ||
V* value = nullptr; | ||
|
||
Entry(const K& key, V* value) : key(key), value(value) | ||
{ | ||
} | ||
|
||
~Entry() | ||
{ | ||
delete value; | ||
} | ||
}; | ||
|
||
Vector<Entry> entries; | ||
|
||
private: | ||
// Copy constructor unsafe, so prevent access | ||
ObjectMap(const ObjectMap<K, V>& that); | ||
}; | ||
|
||
#endif // _SMING_CORE_DATA_OBJECT_MAP_H_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid this (usually) un-desirable side-effect we could take advantage of our
Value
class and use it to create an entry in the map only when a value is actually assigned.