I have a class that acts as a 2D lookup table.
The table is initialized in LookupTable() with the values.
And a value can be retrieved from the table by creating an instance of the table and using the notation table[0][0] which would return 1 in this case.
If you try to look an index that doesn't exist it will throw an exception.
My questions is, I want to define 3 tables not just one. And I don't want to duplicate the class 3 times to do that. Could anybody suggest a way to modify the existing class to support the initialization and lookup of 3 different tables?
class LookupTable {
typedef std::map<int, std::map<int, int>> tableMap;
tableMap mTable;
class proxy {
const std::map<int, int>& mMap;
public:
proxy(const std::map<int, int>& m): mMap(m) {
}
int operator[](int x) const {
std::map<int, int>::const_iterator iter = mMap.find(x);
if (iter == mMap.end()) {
std::cout << "throw exception" << std::endl;
} else {
return iter->second;
}
}
};
public:
LookupTable() {
// initialize table
mTable[0][0] = 1;
mTable[1][1] = 2;
mTable[2][2] = 3;
mTable[3][3] = 4;
}
proxy operator[](int x) const {
std::map<int, std::map<int, int>>::const_iterator iter = mTable.find(x);
if (iter == mTable.end()) {
std::cout << "throw exception" << std::endl;
} else {
return proxy(iter->second);
}
}
};
Instead of having another class perhaps called LookupTableOne where I define another table with different values.
class LookupTableOne {
typedef std::map<int, std::map<int, int>> tableMap;
tableMap mTable;
class proxy {
const std::map<int, int>& mMap;
public:
proxy(const std::map<int, int>& m): mMap(m) {
}
int operator[](int x) const {
std::map<int, int>::const_iterator iter = mMap.find(x);
if (iter == mMap.end()) {
std::cout << "throw exception" << std::endl;
} else {
return iter->second;
}
}
};
public:
LookupTableOne() {
// initialize table
mTable[0][0] = 5;
mTable[1][1] = 6;
mTable[2][2] = 7;
mTable[3][3] = 8;
}
proxy operator[](int x) const {
std::map<int, std::map<int, int>>::const_iterator iter = mTable.find(x);
if (iter == mTable.end()) {
std::cout << "throw exception" << std::endl;
} else {
return proxy(iter->second);
}
}
};
CodePudding user response:
The feature you are implementing with your lookup table is already implemented in both std::map and std::unordered_map (which I would suggest for this problem). It allows const access that fails if the key does not exist and is called std::map::at(Key).
If you construct your lookup map via one of the factories in KamilCuk's answer, you don't need to actually write an extra class and especially not a proxy class:
auto const table = my_first_table(); // returns an unordered_map or map
auto const value = table.at(1).at(1);
If you want the [] syntax, you can still wrap the whole deal in your own class.
However, if you are looking such tables, possibly you are trying to optimise for performance. In that case both flavours of map will be inadequate. A find on a flat std::array (or, if necessary std::vector) will be much faster for small to medium instances. But you should benchmark if performance is an issue.
CodePudding user response:
Do not store variable data initialization in the constructor...
public:
LookupTable() {}
Add a generic initialization function:
LookupTable(const std::map<int, std::map<int, int>>& o) : mTable(o) {}
Create functions that create your lookup table.
LookupTable my_first_table() {
return { {
{ 0, { {0, 1} } },
{ 1, { {1, 2} } },
{ 2, { {2, 3} } },
{ 3, { {3, 4} } },
} };
}
LookupTable my_second_table() {
return { {
{ 0, { {0, 5} } },
{ 1, { {1, 6} } },
{ 2, { {2, 7} } },
{ 3, { {3, 8} } },
} };
}
LookupTable my_second_table() {
return { { .... } };
}
Research factory pattern.
You could also do a template with a parameter or take an argument in the constructor and choose different code path depending on that parameter/argument.
