제목 그대로 선택된 라인들에서 중복된 라인들은 삭제하고 나머지를 정렬하기 위한 매크로입니다.

Visual Assist에 선택된 라인 정렬 기능이 있기는 한데, 중복 라인 삭제는 없어서, 어쩔 수 없이 만들었습니다. 주 목적은 아무래도 C++/include 구문 정리가 되겠습니다.

Collection 객체에 정렬 함수가 따로 없다는 것이 좀 의외군요.

Function Strip(ByVal strLine As String)
    If Len(strLine) > 0 Then       
        nBegin = 1
        nEnd = Len(strLine)
        For i = 1 To Len(strLine)
            c = Mid(strLine, i, 1)
            If c <> " " And c <> Tab And c <> Lf And c <> Cr Then
                nBegin = i
                Exit For
            End If
        Next
        For i = 1 To Len(strLine)
            c = Mid(strLine, Len(strLine) - i + 1, 1)
            If c <> " " And c <> Tab And c <> Lf And c <> Cr Then
                nEnd = Len(strLine) - i + 1
                Exit For
            End If
        Next
        Return Mid(strLine, nBegin, nEnd - nBegin + 1)
    Else
        Return ""
    End If
End Function

Sub SortCollection(ByRef oCollection As Collection, Optional ByVal bSortAscending As Boolean = True)
    Dim lSort1 As Integer
    Dim lSort2 As Integer
    Dim vTempItem1 As Object
    Dim vTempItem2 As Object
    Dim bSwap As Boolean

    For lSort1 = 1 To oCollection.Count - 1
        For lSort2 = lSort1 + 1 To oCollection.Count
            If bSortAscending Then
                If oCollection(lSort1) > oCollection(lSort2) Then
                    bSwap = True
                Else
                    bSwap = False
                End If
            Else
                If oCollection(lSort1) < oCollection(lSort2) Then
                    bSwap = True
                Else
                    bSwap = False
                End If
            End If
            If bSwap Then
                vTempItem1 = oCollection(lSort1)
                vTempItem2 = oCollection(lSort2)
                oCollection.Add(vTempItem1, Nothing, lSort2)
                oCollection.Add(vTempItem2, Nothing, lSort1)
                oCollection.Remove(lSort1 + 1)
                oCollection.Remove(lSort2 + 1)
            End If
        Next
    Next
End Sub

Sub SortAndRemoveDuplicatedLine()
    Dim objLines As New Collection
    Dim objSel As TextSelection = ActiveDocument().Selection
    Dim objRanges As TextRanges = objSel.TextRanges
    Dim objStartPt As EditPoint = objRanges.Item(1).StartPoint.CreateEditPoint()
    Dim objStream As New StringBuilder

    For Each strLine In objSel.Text.Split(Lf)
        strLine = Strip(strLine)
        If objLines.Contains(strLine) = False Then
            objLines.Add(strLine, strLine)
        End If
    Next

    SortCollection(objLines)

    For Each strLine In objLines
        objStream.AppendLine(strLine)
    Next

    objSel.Text = ""
    objStartPt.Insert(objStream.ToString())
End Sub

이 포스트 내용에 대한 이후 업데이트는 http://serious-code.net/moin.cgi/VisualStudioMacro 페이지에서만 이루어질 예정입니다. :)
2009/11/04 16:33 2009/11/04 16:33
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://serious-code.net/tc/rss/response/17

댓글+트랙백 ATOM :: http://serious-code.net/tc/atom/response/17

문자열(std::string)을 키로 가지는 맵 같은 경우, 문자열 비교 자체에 걸리는 시간 때문에 검색이 느려질 수 있다. 이 경우, 키로 사용하는 문자열이 별로 중요한 내용이 아니라면 아래와 같은 클래스를 사용함으로서 성능을 약간 증가시킬 수 있다.

/// \class cStringKey
/// \brief STL 컨테이너를 위한 문자열 키
class cStringKey
{
private:
  enum {
       BYTE_SIZE = 32,
  };

  char m_Text[BYTE_SIZE]; ///< 문자열


public:
  /// \brief 생성자
  cStringKey() {
       memset(m_Text, 0, sizeof(m_Text));
  }

  /// \brief 생성자
  cStringKey(const char* text) {
       memset(m_Text, 0, sizeof(m_Text));
       memcpy_s(m_Text, sizeof(m_Text), text, std::min(sizeof(m_Text), strlen(text)));
  }

  /// \brief 생성자
  cStringKey(const std::string& text) {
       memset(m_Text, 0, sizeof(m_Text));
       memcpy_s(m_Text, sizeof(m_Text), text.c_str(), std::min(sizeof(m_Text), text.size()));
  }

  /// \brief 복사 생성자
  cStringKey(const cStringKey& rhs) {
       memcpy_s(m_Text, sizeof(m_Text), rhs.m_Text, sizeof(m_Text));
  }


public:
  /// \brief 대입 연산자
  inline const cStringKey& operator = (const cStringKey& rhs) {
       if (this != &rhs)
           memcpy_s(m_Text, sizeof(m_Text), rhs.m_Text, sizeof(m_Text));
      return *this;
  }

  /// \brief 비교 연산자
  ///
  /// 이 함수는 약간 유의해야 하는데, 속도를 위해 루프를 풀어버렸기 때문이다.
  /// 클래스의 크기가 변경되면, 이 함수도 같이 변경해줘야 한다.
  inline bool operator < (const cStringKey& rhs) const {
       const int* buf1 = reinterpret_cast<const int*>(this);
       const int* buf2 = reinterpret_cast<const int*>(&rhs);

       if (*buf1 != *buf2) return *buf1 < *buf2; // 0-3

       ++buf1; ++buf2;
       if (*buf1 != *buf2) return *buf1 < *buf2; // 4-7

       ++buf1; ++buf2;
       if (*buf1 != *buf2) return *buf1 < *buf2; // 8-11

       ++buf1; ++buf2;
       if (*buf1 != *buf2) return *buf1 < *buf2; // 12-15

       ++buf1; ++buf2;
       if (*buf1 != *buf2) return *buf1 < *buf2; // 16-19

       ++buf1; ++buf2;
       if (*buf1 != *buf2) return *buf1 < *buf2; // 20-23

       ++buf1; ++buf2;
       if (*buf1 != *buf2) return *buf1 < *buf2; // 24-27

       ++buf1; ++buf2;
       return *buf1 < *buf2; // 28-31
  }
};

대소문자 구별없이 비교를 할 수 없다는 점이 좀 아쉽다. 어셈블리도 좀 안다면 비교 연산자를 좀 더 깔끔하게 만들 수 있을 텐데. 어쨌든 테스트해보니, 릴리즈 빌드에서 약 25~33% 정도의 성능이 향상되었다.

typedef std::map<std::string, std::string> OLD_MAP;
typedef std::map<cStringKey, std::string> NEW_MAP;

OLD_MAP oldMap;
NEW_MAP newMap;

for (int i=0; i<1000; ++i) {
  std::string key = generic::to_string(rand() % 1000, 4);
  std::string value = generic::to_string(rand() % 1000, 4);
  oldMap.insert(OLD_MAP::value_type(key, value));
  newMap.insert(NEW_MAP::value_type(key, value));
}

DWORD begin = 0, oldTime = 0, newTime = 0;
int repetition = 200000;

begin = timeGetTime();
for (int i=0; i<repetition; ++i)
  oldMap.find(generic::to_string(rand() % 1000, 4));
oldTime = timeGetTime() - begin;

begin = timeGetTime();
for (int i=0; i<repetition; ++i)
  newMap.find(generic::to_string(rand() % 1000, 4));
newTime = timeGetTime() - begin;

std::cout << "OLD: " << oldTime << std::endl;
std::cout << "NEW: " << newTime << std::endl;

OLD: 241
NEW: 146

이런 자잘한 최적화 해봐야 얼마나 달라지겠냐만은 기왕이면 다홍치마 아닌가.
2009/09/03 09:07 2009/09/03 09:07
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://serious-code.net/tc/rss/response/11

댓글+트랙백 ATOM :: http://serious-code.net/tc/atom/response/11

코딩하다가 마주치는 일 중에 정말로 귀찮은 일 중의 하나가, 멤버 변수에 대한 Get/Set accessor를 만들어주는 일이다.

Visual Studo 환경에서 작업한다면, 매크로를 통해 이 작업을 어느 정도 자동화시킬 수 있다. 아래에 있는 MakeSimpleGetterSetter 매크로가 이런 작업을 자동화하기 위해 사용하는 매크로다. 멤버 변수들을 복사해서 붙인 다음, 복사한 멤버 변수들을 선택하고, 매크로를 실행하면 된다.

EffectId_t  m_Id;        ///< 이펙트 ID
std::string m_Name;      ///< 이펙트 이름 (디버그용)
bool        m_Visible;   ///< 이펙트가 플레이어에게 보이는가의 여부
bool        m_Removable; ///< 디스펠 스킬로 인해 강제로 제거될 수 있는가의 여부

EffectId_t GetId() const { return m_Id; }
void SetId(EffectId_t value) { m_Id = value; }

const std::string& GetName() const { return m_Name; }
void SetName(const std::string& value) { m_Name = value; }

bool IsVisible() const { return m_Visible; }
void SetVisible(bool value) { m_Visible = value; }

bool IsRemovable() const { return m_Removable; }
void SetRemovable(bool value) { m_Removable = value; }

아 그러고보니, Visual Studio 2005 기준이다. 낮은 버전에는 돌아가지 않을 수도... 아마 돌아가지 않으려나? 옛날부터 조금씩 수정하며 써온 건데... 모르겠군.

멤버 변수 초기화 매크로도 하나 만들어야 한다고 생각은 하고 있는데, 타입에 따라 대입할 값이 틀리다 보니 "생각"만 하고 있다.

Function ParseVariableDeclarations( _
  ByVal strText As String, ByRef colTypes As Collection, ByRef colNames As Collection, ByRef colComments As Collection)
  Dim nSpacePos As Integer
  Dim nSemicolonPos As Integer
  Dim nLineFeedPos As Integer
  Dim bSuccess As Boolean
  Dim strLine As String
  Dim strVariableType As String
  Dim strVariableName As String
  Dim strVariableComment As String

  ' 2개 이상의 스페이스를 하나로 만든다.
  Do
       nSpacePos = InStr(strText, "  ")
       If nSpacePos <> 0 Then
           strText = Replace(strText, "  ", " ")
       Else
           Exit Do
       End If
  Loop

  Do
       ' 더 이상 파싱할 게 없다면 빠져나간다
       If Len(strText) = 0 Then
           Exit Do
       End If

       ' 라인 피드를 찾는다.
       nLineFeedPos = InStr(strText, CStr(CrLf))
       If nLineFeedPos <> 0 Then
           ' 라인 피드가 있다면 현재 라인을 얻어낸다.
           strLine = Trim(Replace(Left(strText, nLineFeedPos - 1), vbTab, " "))
       Else
           ' 라인 피드가 더 이상 없다면...
           strLine = Trim(Replace(strText, vbTab, " "))
           strText = ""
       End If

       ' 라인 제일 처음 나오는 ';' 글자와 ';' 글자 앞에서 제일 처음 나타나는 ' ' 글자를 찾는다.
       bSuccess = False
       nSemicolonPos = InStr(strLine, ";")
       If nSemicolonPos <> 0 Then
           nSpacePos = InStrRev(strLine, " ", nSemicolonPos)
           If nSpacePos <> 0 Then
               bSuccess = True
           End If
       End If

       ' 두 글자 모두를 찾았다면, 변수 선언문이라고 할 수 있다.
       If bSuccess Then
           strVariableType = Trim(Mid(strLine, 1, nSpacePos))
           strVariableName = Trim(Mid(strLine, nSpacePos, nSemicolonPos - nSpacePos))
           strVariableComment = ""

           If nSemicolonPos <> 0 Then
               strVariableComment = Trim(Mid(strLine, nSemicolonPos + 1, Len(strLine) - nSemicolonPos))
           End If

           colTypes.Add(Trim(Replace(strVariableType, vbTab, " ")))
           colNames.Add(Trim(Replace(strVariableName, vbTab, " ")))
           colComments.Add(Trim(Replace(strVariableComment, vbTab, " ")))

           'MsgBox("[" + vtype + "][" + vname + "][" + vcomment + "]")
       End If

       strText = Trim(Right(strText, Len(strText) - nLineFeedPos - 1))
  Loop
End Function

Function GenerateAccessor(ByVal strVarType As String, ByVal strVarName As String, ByVal strMode As String)
  Dim strResult As String

  If strVarType <> "" And strVarName <> "" Then
       Dim objRegex As New System.Text.RegularExpressions.Regex("")
       Dim colBasicTypes As New System.Collections.Generic.Dictionary(Of String, String)
       Dim objStream As New System.Text.StringBuilder

       ' 대문자로만 이루어졌지만, 구조체로 취급하지 않을 타입들 생성
       colBasicTypes.Add("DWORD", "")
       colBasicTypes.Add("BOOL", "")
       colBasicTypes.Add("BYTE", "")
       colBasicTypes.Add("WORD", "")
       colBasicTypes.Add("FLOAT", "")
       colBasicTypes.Add("PFLOAT", "")
       colBasicTypes.Add("PBOOL", "")
       colBasicTypes.Add("LPBOOL", "")
       colBasicTypes.Add("PBYTE", "")
       colBasicTypes.Add("LPBYTE", "")
       colBasicTypes.Add("PINT", "")
       colBasicTypes.Add("LPINT", "")
       colBasicTypes.Add("PWORD", "")
       colBasicTypes.Add("LPWORD", "")
       colBasicTypes.Add("LPLONG", "")
       colBasicTypes.Add("PDWORD", "")
       colBasicTypes.Add("LPDWORD", "")
       colBasicTypes.Add("LPVOID", "")
       colBasicTypes.Add("LPCVOID", "")
       colBasicTypes.Add("INT", "")
       colBasicTypes.Add("UINT", "")
       colBasicTypes.Add("PUINT", "")
       colBasicTypes.Add("WPARAM", "")
       colBasicTypes.Add("LPARAM", "")
       colBasicTypes.Add("LRESULT", "")

       ' 필드 이름 생성
       Dim strFieldName As String
       strFieldName = strVarName
       If Left(strFieldName, 2) = "m_" Then
           strFieldName = Right(strFieldName, Len(strFieldName) - 2)
       End If
       If objRegex.IsMatch(strFieldName, "^[a-z]+[^a-z].+$") Then
           strFieldName = objRegex.Replace(strFieldName, "^([a-z]+)([^a-z].+)$", "$2")
       End If

       ' POD인지 아닌지 체크
       Dim bComplexType = False
       If objRegex.IsMatch(strVarType, "^c[A-Z][A-Za-z0-9]+[^\*\&]$") Then
           ' cDateTime, cTokenizer
           bComplexType = True
       ElseIf objRegex.IsMatch(strVarType, "^[A-Z0-9_]+[^\*\&]$") And Not colBasicTypes.ContainsKey(strVarType) Then
           ' D3DXVECTOR, OVERLAPPED
           bComplexType = True
       ElseIf objRegex.IsMatch(strVarType, "^.+<.+>$") Then
           ' std::vector<int>, cMyTemplate<bool>
           bComplexType = True
       End If

       ' 몇 가지 타입은 따로 처리
       If strVarType = "std::string" Then strVarType = "const std::string&"
       If strVarType = "tstring" Then strVarType = "const tstring&"

       ' Getter? Setter?
       If strMode = "Getter" Then
           If bComplexType Then
               strResult += strVarType + "& Get" + strFieldName + "() { return " + strVarName + "; }" + CStr(Lf)
               strResult += "const " + strVarType + "& Get" + strFieldName + "() const { return " + strVarName + "; }" + CStr(Lf)
           ElseIf strVarType = "bool" Or strVarType = "BOOL" Then
               strResult += strVarType + " Is" + strFieldName + "() const { return " + strVarName + "; }" + CStr(Lf)
           Else
               strResult += strVarType + " Get" + strFieldName + "() const { return " + strVarName + "; }" + CStr(Lf)
           End If
       Else
           If bComplexType Then
               strResult += "void Set" + strFieldName + "(const " + strVarType + "& value) { " + strVarName + " = value; }" + CStr(Lf) + CStr(Lf)
           ElseIf strVarType = "bool" Or strVarType = "BOOL" Then
               strResult += "void Set" + strFieldName + "(" + strVarType + " value) { " + strVarName + " = value; }" + CStr(Lf) + CStr(Lf)
           Else
               strResult += "void Set" + strFieldName + "(" + strVarType + " value) { " + strVarName + " = value; }" + CStr(Lf) + CStr(Lf)
           End If
       End If
  End If

  Return strResult
End Function

Sub MakeSimpleGetterSetter()
  Dim objSel As TextSelection
  Dim colTypes As New Collection
  Dim colNames As New Collection
  Dim colComments As New Collection
  Dim objStream As New System.Text.StringBuilder

  objSel = ActiveDocument().Selection
  ParseVariableDeclarations(Trim(objSel.Text), colTypes, colNames, colComments)

  For i = 1 To colNames.Count
       objStream.Append(GenerateAccessor(colTypes.Item(i), colNames.Item(i), "Getter"))
       objStream.Append(GenerateAccessor(colTypes.Item(i), colNames.Item(i), "Setter"))
  Next

  objSel.Text = objStream.ToString()
End Sub
2009/09/03 09:06 2009/09/03 09:06
받은 트랙백이 없고, 댓글 하나가 달렸습니다.

댓글+트랙백 RSS :: http://serious-code.net/tc/rss/response/9

댓글+트랙백 ATOM :: http://serious-code.net/tc/atom/response/9

VSMacro/ TCHAR 매크로

개발 2009/09/03 09:05 김성민

C 문자열 주위를 _T() 매크로로 감싸주는 Visual Studio 매크로다.

"Some String" ---> _T("Some String")

매크로 IDE(ALT+F11)에서 붙여넣고, 단축키 등록해 준 다음 쓰면 된다. 문자열을 선택한 다음 실행해도 되고, 문자열 가운데 아무데서나 그냥 실행해도 된다.

Sub WrapStringWithTcharMacro()
  Dim objRanges As TextRanges
  Dim objStartPt As EditPoint
  Dim objEndPt As EditPoint

  objRanges = ActiveDocument().Selection.TextRanges
  objStartPt = objRanges.Item(1).StartPoint.CreateEditPoint()
  objEndPt = objRanges.Item(objRanges.Count).EndPoint.CreateEditPoint()

  ' 문자열의 처음을 찾는다.
  While objStartPt.GetText(-1) <> """" Or objStartPt.GetText(-2) = "\"""
       objStartPt.CharLeft(1)
  End While
  objStartPt.CharLeft(1)

  ' 문자열의 끝을 찾는다.
  While objEndPt.GetText(1) <> """" Or objEndPt.GetText(-1) = "\"
       objEndPt.CharRight(1)
  End While
  objEndPt.CharRight(1)

  ' 이미 _T 매크로로 감싸여 있는 문자열이 아니라면 감싸준다.
  If objStartPt.GetText(-3) <> "_T(" Then
       objStartPt.Insert("_T(")
       objEndPt.Insert(")")
  End If
End Sub
2009/09/03 09:05 2009/09/03 09:05
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://serious-code.net/tc/rss/response/8

댓글+트랙백 ATOM :: http://serious-code.net/tc/atom/response/8

C++ 소멸자 이야기

개발 2009/09/03 09:04 김성민
C++ virtual 함수의 동작 방식에 대해 모르는 사람은 드물 거라고 생각한다.
class base
{
public:
  void nvf();               // 1 <----+
  virtual void vf();        // 2      |
  ...                       //        |
};                          //        |
                            //        |
class derived : public base //        |
{                           //        |
public:                     //        |
  void nvf();               // 3      |
  virtual void vf();        // 4 <----|--+
  ...                       //        |  |
};                          //        |  |
                            //        |  |
base* ptr = new derived;    //        |  |
ptr->nvf();                 // 5 -----+  |
ptr->vf();                  // 6 --------+
delete ptr;                 // 7

5번의 경우에는 1번 함수가 호출될 것이고, 6번의 경우에는 4번 함수가 호출될 것이다.

소멸자도 이와 다를 게 없다. 7번에서 소멸자가 virtual 이라면 derived::~derived()가 호출되고, virtual이 아니라면 base::~base()가 호출된다. (자식 클래스에서 소멸자 호출시, 부모 클래스의 소멸자가 연달아 호출된다는 점은 넘어가자.)

즉 위와 같이 derived 클래스를 base 포인터에다 할당하고 삭제하는 경우에는 소멸자를 virtual로 해줘야 한다. 그렇지 않으면 ~derived() 함수가 호출되지 않아, 이 안에 있는 리소스(?) 정리 코드가 호출되지 않기 때문이다. 즉 메모리 릭이 발생할 수 있다. 하지만 아래와 같은 경우에는 굳이 소멸자를 virtual로 만들 필요가 없다.

derived* ptr = new derived();
ptr->nvf();
ptr->vf();
delete ptr;

포인터가 derived* 타입이므로, 소멸자가 virtual이든 아니든 derived::~derived() 함수가 호출되기 때문이다.

즉 소멸자를 virtual로 선언해야 하는지를 판단하기 위해서는, 해당 클래스를 상속받는 클래스가 있느냐/없느냐를 기준으로 삼을 것이 아니라, 실제로 사용할 때 베이스 클래스 포인터를 이용해 접근하는가/아닌가를 기준으로 삼아야 한다.

다만 안타깝게도, 누군가가 작성한 클래스를 다른 사람이 사용한다고 했을 때, 그 사람이 base 포인터를 이용해 작업을 할지, derived 포인터를 이용해서 작업을 할지 최초 작성자는 알 수 없다. 결국  안전빵(?)으로 가기 위해 소멸자는 무조건 virtual로 선언하는 경우가 많다.

일반적인 PC 환경에서 virtual 소멸자 마구 쓴다고 해서 딱히 크게 문제될 것은 없다. 인스턴스당 4바이트 정도의 메모리가 더 들어가고, vtable 또한 어딘가에 생성되겠지만, 시대가 어떤 시댄데...

다만 C 구조체를 상속받아서 그 구조체를 인자로 받는 함수로 전달하거나, 객체를 통채로 직렬화하려는 경우에는 주의해야 한다.

2009/09/03 09:04 2009/09/03 09:04
받은 트랙백이 없고, 댓글 하나가 달렸습니다.

댓글+트랙백 RSS :: http://serious-code.net/tc/rss/response/6

댓글+트랙백 ATOM :: http://serious-code.net/tc/atom/response/6

컨테이너를 횡단하면서 아이템 삭제하기에 관한 글은 Effective C++에 잘 나와있다.

순차형(sequential) 컬렉션

typedef std::list<OBJECT*> OBJECTS;
OBJECTS objects;
...
for (OBJECTS::iterator itr(objects.begin()); itr != object.end(); )
{
  OBJECT* object = *itr;

  if (should_delete(object))
  {
     itr = objects.erase(itr);
     delete object;
  }
  else itr++;
}

연관형(associative) 컬렉션

typedef std::map<int, OBJECT*> OBJECTS;
OBJECTS objects;
...
for (OBJECTS::iterator itr(objects.begin()); itr != object.end(); )
{
  OBJECT* object = itr->second;

  if (should_delete(object))
  {
     objects.erase(itr++);
     delete object;
  }
  else itr++;
}

문제는 둘이 비슷비슷해서 자꾸 착각한다는 것. 컨테이너가 어떤 것이냐와는 상관없이 아이템을 삭제하고 싶었다. 더불어 컨테이너에 들어있는 것이 일반(?) 변수인지 포인터 변수인지도 신경쓰고 싶지 않았다. 뜻이 있는 곳에 길이 있는 법.

struct YES {};
struct NO {};

template <typename T>
struct is_ptr
{
  enum { result = false };
  typedef NO Result;
};

template <typename T>
struct is_ptr<T*>
{
  enum { result = true };
  typedef YES Result;
};

template <typename T>
struct is_ptr<T* const>
{
  enum { result = true };
  typedef YES Result;
};

template <typename T>
struct is_ptr<T* volatile>
{
  enum { result = true };
  typedef YES Result;
};

template <typename T>
struct is_ptr<T* const volatile>
{
  enum { result = true };
  typedef YES Result;
};

template <typename T>
class is_pair
{
private:
  template <typename U>
  struct is_pair_imp
  {
     enum { value = false };
     typedef NO Result;
  };

  template <typename U, typename V>
  struct is_pair_imp < std::pair<U, V> >
  {
    enum { value = true };
     typedef YES Result;
  };

public:
  enum { value = is_pair_imp<T>::value };
  typedef typename is_pair_imp<T>::Result Result;
};

template <typename IS_POINTER>
struct do_delete {};

template <>
struct do_delete<YES>
{
  template <typename U>
  void operator()(U param) { delete param; }
};

template <>
struct do_delete<NO>
{
  template <typename U>
  void operator()(U param) {}
};
      
template <typename IS_ASSOCIATION>
struct delete_if_imp {};

template <>
struct delete_if_imp<YES>
{
  template <typename C, typename P>
  void operator()(C& con, P comp)
  {
     do_delete< is_ptr<C::value_type::second_type>::Result > doDelete;
     for (C::iterator itr(con.begin()); itr != con.end();)
     {
         if (comp(itr->second))
         {
             doDelete(itr->second);
             con.erase(itr++);
         }
         else ++itr;
     }
  }
};

template <>
struct delete_if_imp<NO>
{
  template <typename C, typename P>
  void operator()(C& con, P comp)
  {
     do_delete< is_ptr<C::value_type>::Result > doDelete;
     for (C::iterator itr(con.begin()); itr != con.end();)
     {
         if (comp(*itr))
         {
             doDelete(*itr);
             itr = con.erase(itr);
         }
         else ++itr;
     }
  }
};

template <typename C, typename P>
inline void delete_if(C& con, P comp)
{
  delete_if_imp< is_pair<C::value_type>::Result > imp;
  imp(con, comp);
}

하는 일에 비해 코드가 꽤 길어졌다. 뭐 어쨌든 최종 결과물인 delete_if 함수를 이용하면 컨테이너의 종류와 상관없이 아이템을 삭제할 수 있다.

class test
{
private:
  int myValue;

public:
  test(int value = 0) : myValue(value) {}
  ~test()
  {
  //std::cout << myValue << " dying" << std::endl;
  }
};

struct COMPARE_TEST
{
  bool operator()(test* t) { return t->myValue < 2; }
  bool operator()(test& t) { return t.myValue < 2; }
};

int main()
{
  std::map<int, test*> myMap;
  std::map<int, test> myMap2;
  std::vector<test*> myVector;

  myMap[0] = new test(0);
  myMap[1] = new test(1);
  myMap[2] = new test(2);
  myMap[3] = new test(3);

  myMap2[0] = test(0);
  myMap2[1] = test(1);
  myMap2[2] = test(2);
  myMap2[3] = test(3);

  myVector.push_back(new test(0));
  myVector.push_back(new test(1));
  myVector.push_back(new test(2));
  myVector.push_back(new test(3));

  std::cout << ">> MAP BEFORE: " << myMap.size() << std::endl;
  delete_if(myMap, COMPARE_TEST());
  std::cout << ">> MAP AFTER: " << myMap.size() << std::endl;

  std::cout << ">> MAP2 BEFORE: " << myMap2.size() << std::endl;
  delete_if(myMap2, COMPARE_TEST());
  std::cout << ">> MAP2 AFTER: " << myMap2.size() << std::endl;

  std::cout << ">> VECTOR BEFORE: " << myVector.size() << std::endl;
  delete_if(myVector, COMPARE_TEST());
  std::cout << ">> VECTOR AFTER: " << myVector.size() << std::endl;

  return 0;
}

 

2009/09/03 08:54 2009/09/03 08:54
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://serious-code.net/tc/rss/response/5

댓글+트랙백 ATOM :: http://serious-code.net/tc/atom/response/5