Maps¶
Introduction¶
A FSTR::Map
is analogous to the Wiring HashMap
class, allowing content to be indexed using
a key value.
The Map contains an array of FSTR::MapPair
structures:
struct MapPair<KeyType, ContentType> {
KeyType key_;
ContentType* content_;
};
KeyType
can be any simple type such as char
, int
, float
, enum
etc.
It may also be a String
Object (or, more precisely, String*
).
ContentType
can be any Object type (String, Array, Vector or Map).
This allows hierarchical structures to be created.
Example: int ⇒ String¶
Here’s a basic example using integer keys:
#include <FlashString/Map.hpp>
IMPORT_FSTR_LOCAL(content1, PROJECT_DIR "/files/index.html");
IMPORT_FSTR_LOCAL(content2, PROJECT_DIR "/files/favicon.html");
DEFINE_FSTR_MAP(intmap, int, FSTR::String,
{35, &content1},
{180, &content2}
);
You should generally use IMPORT_FSTR_LOCAL()
when referencing imported objects.
If you need global access to imported data as well, then use IMPORT_FSTR()
.
Note
Older toolchains (generally GCC earlier than version 6) will fail to compile with error: the value of ‘FS_content1’ is not usable in a constant expression.
You can work around this as follows:
IMPORT_FSTR(content1, PROJECT_DIR "/files/index.html"); // Make this a global reference
IMPORT_FSTR_LOCAL(content2, PROJECT_DIR "/files/favicon.html");
DEFINE_FSTR_MAP(intmap, int, FSTR::String,
{35, &FSTR_DATA_NAME(FS_content1).as<FSTR::String>()}, // Cast the actual content
{180, &content2}
);
We can now do this:
void printValue(int key)
{
auto value = intmap[key];
if(value) {
Serial.printf("Found '%u' in map, containing %u chars\n", value.key(), value.content().length());
Serial.println(value.printer());
} else {
Serial.printf("Couldn't find '%u' in map\n", key);
}
}
Example: String ⇒ String¶
Both the key and the content are stored as Strings:
#include <FlashString/Map.hpp>
DEFINE_FSTR_LOCAL(key1, "index.html");
DEFINE_FSTR_LOCAL(key2, "favicon.ico");
IMPORT_FSTR_LOCAL(content1, PROJECT_DIR "/files/index.html");
IMPORT_FSTR_LOCAL(content2, PROJECT_DIR "/files/favicon.html");
DEFINE_FSTR_MAP(fileMap, FlashString, FlashString,
{&key1, &content1},
{&key2, &content2},
);
We can now do this:
void onFile(HttpRequest& request, HttpResponse& response)
{
String fileName = request.uri.getRelativePath();
auto& value = fileMap[fileName];
if(value) {
// Found
Serial.printf("Found '%s' in fileMap\n", String(value.key()).c_str());
auto stream = new FlashMemoryStream(value);
response.sendDataStream(stream, ContentType::fromFullFileName(fileName));
} else {
Serial.printf("File '%s' not found\n", fileName.c_str());
}
}
Note
As with Vector<String>
, Map<String, ...>
lookups are by default case-insensitive.
If you require a case-sensitive lookup, use the indexOf
method with ignoreCase = false
.
Structure¶
The macro in the first example above produces a structure like this:
constexpr const struct {
ObjectBase object;
MapPair<int, String> data[2];
} __fstr__intmap PROGMEM = {
{16},
{35, &content1},
{180, &content2},
};
const Map<int, String>& intmap = __fstr__intmap.object.as<Map<int, String>>();
Note: FSTR::
namespace qualifier omitted for clarity.
Usually, each MapPair is 8 bytes, but if the key is a double or int64 it would be 12 bytes.
Macros¶
-
DECLARE_FSTR_MAP
(name, KeyType, ContentType)¶ Declare a global Map& reference.
- Note
Use DEFINE_FSTR_MAP to instantiate the global object
- Parameters
name
: Name of the Map& reference to defineKeyType
: Integral type to use for keyContentType
: Object type to declare for content
-
DEFINE_FSTR_MAP
(name, KeyType, ContentType, ...)¶ Define a Map Object with global reference.
- Note
Size will be calculated
- Parameters
name
: Name of the Map& reference to defineKeyType
: Integral type to use for keyContentType
: Object type to declare for content...
: List of MapPair definitions { key, &content }
-
DEFINE_FSTR_MAP_LOCAL
(name, KeyType, ContentType, ...)¶ Like DEFINE_FSTR_MAP except reference is declared static constexpr.
-
DEFINE_FSTR_MAP_SIZED
(name, KeyType, ContentType, size, ...)¶ Define a Map Object with global reference, specifying the number of elements.
- Parameters
name
: Name of the Map& reference to defineKeyType
: Integral type to use for keyContentType
: Object type to declare for contentsize
: Number of elements...
: List of MapPair definitions { key, &content }
-
DEFINE_FSTR_MAP_SIZED_LOCAL
(name, KeyType, ContentType, size, ...)¶ Like DEFINE_FSTR_MAP_SIZED except reference is declared static.
-
DEFINE_FSTR_MAP_DATA
(name, KeyType, ContentType, ...)¶ Define a Map data structure.
- Note
Size will be calculated
- Parameters
name
: Name of data structureKeyType
: Integral type to use for keyContentType
: Object type to declare for content...
: List of MapPair definitions { key, &content }
-
DEFINE_FSTR_MAP_DATA_SIZED
(name, KeyType, ContentType, size, ...)¶ Define a Map data structure, specifying the number of elements.
- Parameters
name
: Name of data structureKeyType
: Integral type to use for keyContentType
: Object type to declare for contentsize
: Number of elements...
: List of MapPair definitions { key, &content }
-
template<typename
KeyType
, classContentType
>
classMapPair
¶ - #include <MapPair.hpp>
describes a pair mapping key => data for a specified key type
- Template Parameters
KeyType
: Integral, floating point, enum or StringContentType
: Object type to use for content
Class Templates¶
-
template<typename
KeyType
, classContentType
, classPair
= MapPair<KeyType, ContentType>>
classMap
: public FSTR::Object<Map<KeyType, ContentType>, Pair>¶ Class template to access an associative map.
- Template Parameters
KeyType
:
Public Functions
-
const Pair
valueAt
(unsigned index) const¶ Get a map entry by index, if it exists.
- Note
Result validity can be checked using if()
-
template<typename
TRefKey
, typenameT
= KeyType>
std::enable_if<!std::is_class<T>::value, int>::typeindexOf
(const TRefKey &key) const¶ Lookup an integral key and return the index.
- Parameters
key
: Key to locate, must be compatible with KeyType for equality comparison
- Return Value
int
: If key isn’t found, return -1
-
template<typename
TRefKey
, typenameT
= KeyType>
std::enable_if<std::is_same<T, String>::value, int>::typeindexOf
(const TRefKey &key, bool ignoreCase = true) const¶ Lookup a String key and return the index.
- Parameters
key
:ignoreCase
: Whether search is case-sensitive (default: true)
- Return Value
int
: If key isn’t found, return -1
-
template<typename
TRefKey
>
const Pairoperator[]
(const TRefKey &key) const¶ Lookup a key and return the entry, if found.
- Note
Result validity can be checked using if()
- Parameters
key
:
-
MapPrinter<Map>
printer
() const¶ Returns a printer object for this array.
- Note
ElementType must be supported by Print
-
template<typename
KeyType
, classContentType
>
classMapPair
describes a pair mapping key => data for a specified key type
- Template Parameters
KeyType
: Integral, floating point, enum or StringContentType
: Object type to use for content
Public Functions
-
operator IfHelperType
() const¶ Provides bool() operator to determine if Pair is valid.
-
template<typename
T
= KeyType>
std::enable_if<!std::is_class<T>::value, KeyType>::typekey
() const¶ Get the key (non-class key types)
-
template<typename
T
= KeyType>
std::enable_if<std::is_same<T, String>::value, const KeyType&>::typekey
() const¶ Get the key (String key type)
-
const ContentType &
content
() const¶ Accessor to get a reference to the content.
Public Static Functions
-
static const MapPair
empty
()¶ Get an empty Pair object, identifies as invalid when lookup fails.