라이브러리를 만들어 볼까? – 검색용 2차원 표 템플릿

프로그램을 만들다 보면 switch 문을 2중으로 쓸 일이 있습니다. 물론 switch 문에 사용할 정보를 문자열로 만들어 if 문으로 바꾸는 방법도 생각해 볼 수 있지만 조건문이 많아지는 것도 별로라 이럴 때는 검색용 표를 쓰는 게 좋을 듯합니다. 즉 검색용 2차원 표를 만들고 각 조건별로 호출할 함수 포인터를 표에 저정한 다음 switch 문에서 비교할 정보 둘을 색인으로 바로 호출하는 거죠. 그래서 2차원 배열 내지 표가 필요한데 단순하게 내장 배열이나 vector로 구현하는 것에는 문제가 있습니다. 비교할 정보가 연속적이지 않으므로 불필요한 공간이 생길 수 있다는 거죠. 그래서 map을 사용해 구현해 보기로 했습니다.

#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

색인과 값으로 사용할 타입을 지정할 수 있도록 템플릿으로 만들었습니다. 사용은 다음처럼 합니다.

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++에 있는 기본 배열이 맘에 안 드는 분은 부스트 다차원 배열을 쓰면 좋을 것 같습니다.

Notes:
1. 물론 ‘절대’ 귀찮아서가 아닙니다.
물론 ‘절대’ 귀찮아서가 아닙니다.

You may also like...