문제

최근 일부 레거시 코드를 리팩토링하기 시작했고 좌표 그리드를 그리는 두 가지 함수를 발견했습니다. 문제는 이러한 함수가 처리하는 직교 변수에서만 다르다는 것입니다.

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
         MoveToEx(dc, x, y0, NULL);
         LineTo(dc, x, y1);
    }
}
void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
         MoveToEx(dc, x0, y, NULL);
         LineTo(dc, x1, y);
    }
}

따라서 앤티앨리어싱과 같은 멋진 기능을 추가하기로 결정하거나 그리기 연필만 변경하는 등의 작업을 수행하기로 결정하면 둘 다에 동일한 코드를 입력해야 하며 이는 코드 중복이며 우리 모두가 그 이유를 아는 것은 좋지 않습니다.

내 질문은 이 문제를 피하기 위해 이 두 함수를 단일 함수로 어떻게 다시 작성하겠습니까?

도움이 되었습니까?

해결책

선을 그리는 것은 단순히 두 점을 연결하고 X 및/또는 Y를 통해 특정 방향으로 (x0,y0) 및 (x1,y1)을 증가시키는 크기 조정을 그리는 것입니다.이는 규모의 경우 어느 방향으로 스테핑이 발생하는지에 따라 결정됩니다(재미상 양방향일 수도 있음).

template< int XIncrement, YIncrement >
struct DrawScale
{
  void operator()(HDC dc, int step, int x0, int x1, int y0, int y1)
  {
    const int deltaX = XIncrement*step;
    const int deltaY = YIncrement*step;
    const int ymax = y1;
    const int xmax = x1;
    while( x0 < xmax && y0 < ymax )
    {
        MoveToEx(dc, x0, y0, NULL);
        LineTo(dc, x1, y1);
        x0 += deltaX;
        x1 += deltaX;
        y0 += deltaY;
        y1 += deltaY;
    }
  }
};
typedef DrawScale< 1, 0 > DrawScaleX;
typedef DrawScale< 0, 1 > DrawScaleY;

템플릿은 해당 작업을 수행합니다.컴파일 타임에 컴파일러는 모든 null 문을 제거합니다.deltaX 또는 deltaY는 어떤 함수가 호출되는지에 대해 0이고 각 펑터에서 코드의 절반이 사라집니다.

이 uniq 함수 안에 앤티앨리어싱, 연필 등을 추가하면 컴파일러에서 생성된 코드를 올바르게 생성할 수 있습니다.

이건 잘라서 스테로이드에 붙여넣은 거예요 ;-)

-- ppi

다른 팁

왜 for 사이클의 본문을 별도의 함수로 추출하지 않습니까?그런 다음 추출된 함수에서 재미있는 작업을 수행할 수 있습니다.

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int x = x0; x < x1; x += step)
    {
        DrawScale(dc, x, y0, x, y1);
    }
}

void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    for(int y = y0; y < y1; y += step)
    {
        DrawScale(dc, x0, y, x1, y);
    }
}

private void DrawScale(HDC dc, int x0, int y0, int x1, int y1)
{
    //Add funny stuff here

    MoveToEx(dc, x0, y0, NULL);
    LineTo(dc, x1, y1);

    //Add funny stuff here
}

내 자신의 솔루션은 다음과 같습니다.


class CoordGenerator
{
public:
    CoordGenerator(int _from, int _to, int _step)
        :from(_from), to(_to), step(_step), pos(_from){}
    virtual POINT GetPoint00() const = 0;
    virtual POINT GetPoint01() const = 0;
    bool Next()
        {
            if(pos > step) return false;
            pos += step;
        }
protected:
    int from;
    int to;
    int step;
    int pos;
};

class GenX: public CoordGenerator
{
public:
    GenX(int x0, int x1, int step, int _y0, int _y1)
        :CoordGenerator(x0, x1, step),y0(_y0), y1(_y1){}
    virtual POINT GetPoint00() const
        {
            const POINT p = {pos, y0};
            return p;
        }
    virtual POINT GetPoint01() const
        {
            const POINT p = {pos, y1};
            return p;
        }
private:
    int y0;
    int y1;
};

class GenY: public CoordGenerator
{
public:
    GenY(int y0, int y1, int step, int _x0, int _x1)
        :CoordGenerator(y0, y1, step),x0(_x0), x1(_x1){}
    virtual POINT GetPoint00() const
        {
            const POINT p = {x0, pos};
            return p;
        }
    virtual POINT GetPoint01() const
        {
            const POINT p = {x1, pos};
            return p;
        }
private:
    int x1;
    int x0;
};

void DrawScale(HDC dc, CoordGenerator* g)
{
    do
    {
        POINT p = g->GetPoint00();
        MoveToEx(dc, p.x, p.y, 0);
        p = g->GetPoint01();
        LineTo(dc, p.x, p.y);
    }while(g->Next());
}

하지만 그렇게 작은 문제에 비해 너무 복잡한 것 같아서 계속해서 귀하의 솔루션을 볼 수 있기를 기대합니다.

글쎄, 분명한 "해결책"은 단일 함수를 만들고 (열거형 유형의) 추가 매개변수 하나를 추가하는 것입니다.그런 다음 내부에서 if() 또는 switch()를 수행하고 적절한 작업을 수행합니다.왜냐면 이봐, 기능성 기능이 다르기 때문에 서로 다른 작업을 수행해야 합니다. 어딘가에.

그러나 이는 컴파일 타임에 더 잘 확인할 수 있는 위치에 런타임 복잡성(런타임에 항목 확인)을 추가합니다.

앞으로 둘 다(또는 더 많은 기능)에 추가 매개변수를 추가할 때 문제가 무엇인지 이해할 수 없습니다.다음과 같이 진행됩니다.

  1. 모든 기능에 더 많은 매개변수 추가
  2. 코드를 컴파일하면 새 매개변수를 전달하지 않기 때문에 여러 위치에서 컴파일되지 않습니다.
  3. 새 매개변수를 전달하여 해당 함수를 호출하는 모든 위치를 수정하세요.
  4. 이익!:)

C++인 경우 물론 함수를 템플릿으로 만들고 추가 매개변수를 추가하는 대신 템플릿 매개변수를 추가한 다음 템플릿 구현을 특수화하여 다른 작업을 수행할 수 있습니다.그러나 내 생각에는 이것은 요점을 모호하게 만드는 것일 뿐이다.코드는 이해하기 어려워지고, 더 많은 매개변수로 코드를 확장하는 과정은 여전히 정확히 똑같다:

  1. 추가 매개변수 추가
  2. 코드를 컴파일하면 여러 곳에서 컴파일되지 않습니다.
  3. 해당 기능을 호출하는 모든 장소를 수정하세요.

따라서 아무것도 얻지 못했지만 코드를 이해하기 어렵게 만들었습니다.가치 있는 목표가 아닙니다, IMO.

나는 이사할 것 같아요:

     MoveToEx(dc, x0, y, NULL);
     LineTo(dc, x1, y);

각각의 기존 함수에서 호출할 수 있는 자체 함수 DrawLine(x0,y0,x0,y0)으로 변환합니다.

그렇다면 추가적인 그리기 효과를 추가할 수 있는 곳이 있나요?

약간의 템플릿...:)

void DrawLine(HDC dc, int x0, int y0, int x0, int x1)
{
    // anti-aliasing stuff
    MoveToEx(dc, x0, y0, NULL);
    LineTo(dc, x1, y1);
}

struct DrawBinderX
{
    DrawBinderX(int y0, int y1) : y0_(y0), y1_(y1) {}

    void operator()(HDC dc, int i)
    {
        DrawLine(dc, i, y0_, i, y1_);
    }

private:
    int y0_;
    int y1_;

};

struct DrawBinderY
{
    DrawBinderX(int x0, int x1) : x0_(x0), x1_(x1) {}

    void operator()(HDC dc, int i)
    {
        DrawLine(dc, x0_, i, x1_, i);
    }

private:
    int x0_;
    int x1_;

};

template< class Drawer >
void DrawScale(Drawer drawer, HDC dc, int from, int to, int step)
{
    for (int i = from; i < to; i += step)
    {
         drawer(dc, i);
    }
}

void DrawScaleX(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    DrawBindexX drawer(y0, y1);
    DrawScale(drawer, dc, x0, x1, step);
}

void DrawScaleY(HDC dc, int step, int x0, int x1, int y0, int y1)
{
    DrawBindexY drawer( x0, x1 );
    DrawScale(drawer, dc, y0, y1, step);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top