라이브러리를 만들어 볼까? – 검색용 2차원 표 템플릿
프로그램을 만들다 보면 switch
문을 2중으로 쓸 일이 있습니다. 물론 switch
문에 사용할 정보를 문자열로 만들어 if
문으로 바꾸는 방법도 생각해 볼 수 있지만 조건문이 많아지는 것도 별로라 이럴 때는 검색용 표를 쓰는 게 좋을 듯합니다. 즉 검색용 2차원 표를 만들고 각 조건별로 호출할 함수 포인터를 표에 저정한 다음 switch
문에서 비교할 정보 둘을 색인으로 바로 호출하는 거죠. 그래서 2차원 배열 내지 표가 필요한데 단순하게 내장 배열이나 vector
로 구현하는 것에는 문제가 있습니다. 비교할 정보가 연속적이지 않으므로 불필요한 공간이 생길 수 있다는 거죠. 그래서 map
을 사용해 구현해 보기로 했습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
#ifndef MAP_TABLE_2D_H_ #define MAP_TABLE_2D_H_ #include <exception> #include <map> namespace surp_lib { /** * 2D map table. * @tparam T1 subscript type. * @tparam T2 object type. */ template <typename T1, typename T2> class MapTable2D { private: using InnerMap = std::map<T1, T2>; using MapTable = std::map<T1, InnerMap>; public: /** * proxy class. */ class MapTable1D { public: /** * constructor. */ explicit MapTable1D(InnerMap& innerMap) : innerMap_(innerMap) {} /** * overloaded operator[]. * @param[in] index subscript. * @return reference to value object. */ T2& operator[](size_t index) { auto pos = innerMap_.find(index); if (innerMap_.end() != pos) { return pos->second; } else { throw std::runtime_error("Invalid second subscript"); } } private: InnerMap& innerMap_; }; /** * overloaded operator[]. * @param[in] index subscript. * @return reference to proxy object. */ MapTable1D operator[](size_t index) { auto pos = mapTable_.find(index); if (mapTable_.end() != pos) { return MapTable1D(pos->second); } else { throw std::runtime_error("Invalid first subscript"); } } /** * insert element. * @param[in] x subscript x. * @param[in] y subscript y. * @param[in] value value to insert. */ void insert(T1 x, T1 y, T2 const& value) { auto posX = mapTable_.find(x); if (mapTable_.end() != posX) { // exist InnerMap& innerMap = posX->second; innerMap.insert(std::make_pair(y, value)); } else { // not exist InnerMap innerMap; innerMap.insert(std::make_pair(y, value)); mapTable_.insert(std::make_pair(x, innerMap)); } } /** * update element. * @param[in] x subscript x. * @param[in] y subscript y. * @param[in] value value to insert. */ void update(T1 x, T1 y, T2 const& value) { auto posX = mapTable_.find(x); if (mapTable_.end() != posX) { // exist InnerMap& innerMap = posX->second; auto posY = innerMap.find(y); if (innerMap.end() != posY) { posY->second = value; } else { throw std::runtime_error("Invalid second subscript"); } } else { // not exist throw std::runtime_error("Invalid first subscript"); } } private: MapTable mapTable_; ///< map table }; } #endif |
색인과 값으로 사용할 타입을 지정할 수 있도록 템플릿으로 만들었습니다. 사용은 다음처럼 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
int main() { class Test { public: virtual int print() = 0; }; class Test1 : public Test { public: int print() { return 987; } }; class Test2 : public Test { public: int print() { return 123; } }; Test1 test1; Test2 test2; using surp_lib::MapTable2D; MapTable2D<int, Test*> mapTable; mapTable.insert(1, 13, &test1); mapTable.insert(5, 1, &test2); Test* test = mapTable[1][13]; test->print(); test = mapTable[5][1]; test->print(); MapTable2D<int, int> mapT; mapT.insert(1, 4, 14); mapT.insert(1, 3, 13); mapT.insert(2, 7, 27); mapT[1][4]; mapT[1][3]; mapT[2][7]; mapT.update(1, 4, 30); } |
만든 표 내용을 계속 바꾸기보다 한 번 만든 후 검색에만 주로 사용할 것이므로 삭제 기능은 없습니다만 갱신은 그냥 추가해 봤습니다. 그런데 정보 추가할 때 []
를 못 쓰고 insert()
, update()
등 함수를 써야 하는 점이 맘에 안들어 찾아봤더니…
부스트에 다차원 배열이 있네요. 😯
템플릿이며 메모리를 동적으로 할당하므로 차원과 값 타입을 원하는 대로 지정할 수 있습니다. 하지만 처음에 밝힌, 색인이 연속적이지 않을 수 있다는 조건에는 맞지 않아 보여 부스트 다차원 배열 사용은 다음 기회로 미루고 직접 만든 템플릿을 써야겠습니다. C++에 있는 기본 배열이 맘에 안 드는 분은 부스트 다차원 배열을 쓰면 좋을 것 같습니다.