programing

오래된 C 코드를 업데이트하는 방법은?

telebox 2023. 9. 24. 12:46
반응형

오래된 C 코드를 업데이트하는 방법은?

저는 이번 주에 제 직장에서 10년 된 C 코드 작업을 하고 있는데, 몇 가지 변경 사항을 시행한 후 사장님께 가서 다른 작업이 필요한지 물었습니다.그때 그가 폭탄을 떨어뜨렸어요.저의 다음 과제는 7000여개의 라인을 살펴보고 코드를 좀 더 이해하고 코드를 다소 모듈화하는 것이었습니다.소스코드 모듈화를 어떻게 해주면 좋겠냐고 물었더니 C++ 클래스에 기존 C코드를 넣으라고 합니다.

일을 잘하는 저는 고개를 끄덕이고는, 도대체 어떻게 이 코드를 받아들일지, 그리고 그것을 "모듈화"할 수 있을까 고민하며, 지금 제가 앉아 있는 제 책상으로 돌아갔습니다.각각의 목적과 기능을 가진 20개의 소스 파일에 이미 포함되어 있습니다.또한, 세 개의 "주요" 구조가 있습니다.이 구조물들 각각은 30개 이상의 필드들을 가지고 있으며, 대부분은 다른 작은 구조물들입니다.이해하기에는 완전히 엉망이지만 프로그램의 거의 모든 기능은 구조 중 하나에 포인터를 전달하고 구조를 많이 사용합니다.

제가 이걸 수업에 도입할 수 있는 깨끗한 방법이 없을까요?할 수 있다면 해야겠다고 다짐하고 있는데, 어떻게 시작해야 할지 막막합니다.

첫째, 코드 리팩토링이 장기적인 비용 절감 전략이 될 수 있음을 인식하는 상사가 있어 다행입니다.

나는 이것을 여러 번 해본 적이 있습니다. 즉, 오래된 C 코드를 C++로 변환하는 것입니다.그 혜택은 여러분을 놀라게 할 것입니다.작업이 끝나면 최종 코드는 원래 크기의 절반이 될 수 있으며 읽기가 훨씬 간단합니다.게다가, 당신은 그 과정에서 까다로운 C 버그를 발견할 가능성이 높습니다.여기 당신의 경우에 제가 취할 조치들이 있습니다.코드의 큰 몸체를 리팩토링할 때 A에서 Z로 점프할 수 없기 때문에 작은 단계가 중요합니다.결코 배포할 수는 없지만 사용 중인 RCS에서 검증되고 태그가 지정될 수 있는 소규모 중간 단계를 거쳐야 합니다.

  1. 회귀 분석/테스트 제품군을 만듭니다.코드에 대한 일괄 변경을 완료할 때마다 테스트 제품군을 실행합니다.이것은 이미 가지고 있어야 하며, 단지 이 리팩토링 작업 이상의 작업에 유용할 것입니다.시간을 들여 포괄적으로 만들어 보세요.테스트 스위트를 작성하는 연습을 통해 코드에 익숙해질 것입니다.
  2. 선택한 수정판 제어 시스템에서 프로젝트를 분기합니다.테스트 스위트와 운동장 지점을 갖춘 당신은 코드를 크게 수정할 수 있는 권한을 갖게 될 것입니다.계란 깨는 것을 두려워하지는 않을 것입니다.
  3. 구조 필드를 비공개로 설정합니다.이 단계에서는 코드 변경이 거의 필요하지 않지만 큰 보상을 받을 수 있습니다.한 번에 하나의 필드를 진행합니다.각 필드를 만들어 봅니다.private(예 또는 보호됨)을 선택한 다음 해당 필드에 액세스하는 코드를 분리합니다.이지 않은 를입니다로 입니다.friend function해 봅니다 그 코드를 하나의 방법으로 만드는 것도 고려해 보세요.코드를 메소드로 변환하는 것은 간단하지만, 콜 사이트도 모두 변환해야 합니다.하나가 반드시 다른 하나보다 나은 것은 아닙니다.
  4. 파라미터를 각 함수로 좁힙니다.어떤 함수도 자신의 인수로 통과된 구조물의 30개 필드에 모두 접근할 필요는 없을 것입니다.전체 구조물을 전달하는 대신 필요한 구성요소만 전달합니다.함수가 실제로 구조의 여러 다른 필드에 액세스해야 하는 것처럼 보이는 경우, 이는 인스턴스 메소드로 변환하기에 좋은 후보가 될 수 있습니다.
  5. 변수, 모수 및 방법을 가능한 한 많이 정의합니다.오래된 C 코드를 사용하지 못하는 경우가 많습니다.const대범하게 아래쪽에서 위로 올리면 되고, 할 수 .아래쪽부터 위로 쓸어 올리면(통화 그래프의 아래쪽), 코드에 더 강력한 보장이 추가되고, 비변형자로부터 변이자를 식별할 수 있습니다.
  6. 포인터를 적절한 경우 참조로 바꿉니다.이 단계의 목적은 단지 더 많은 C++를 위해 더 많은 C++를 갖는 것과는 아무 관련이 없습니다.목적은 결코 그렇지 않은 파라미터를 식별하는 것입니다.NULL다시는 assigned할 수 없을 겁니다참조는 유효한 개체에 대한 별칭이며 현재 범위 전체에서 동일한 개체를 나타내는 컴파일 시간 주장이라고 생각합니다.
  7. 로 대체합니다.이 단계는 분명해야 합니다.코드 라인을 크게 줄일 수 있습니다.또한 10줄의 코드를 한 줄로 바꾸는 재미가 있습니다.C++에서 표준인 C 문자열 연산을 수행하는 것이 목적인 전체 함수를 제거할 수도 있습니다.
  8. C 배열을 또는변환합니다. 다시 말하지만, 이 단계는 분명해야 합니다.이 변환은 에서 변환하는 것보다 훨씬 간단합니다.char.std::stringf의 때문입니다.std::vector그리고.std::arrayC 배열 구문과 일치하도록 설계되었습니다. 중 다를 할 수 것입니다.length변수는 배열과 함께 모든 함수에 전달됩니다.
  9. /free로 변환합니다.delete이 단계의 주된 목적은 미래의 리팩토링에 대비하는 것입니다.단지 C 코드를 에서 변경하는 것.malloc.new직접적으로 많은 것을 얻지는 못합니다.이 변환을 통해 구조물에 생성자와 생성자를 추가하고 내장된 C++ 자동 메모리 도구를 사용할 수 있습니다.
  10. 패밀리와 함께 로컬라이제이션/delete작업을 대체합니다.이 단계의 목적은 코드를 예외적으로 안전하게 만드는 것입니다.
  11. 반환 코드가 처리되는 모든 곳에 버블링을 통해 예외를 적용합니다.C 코드가 특별한 오류 코드를 확인한 다음 오류 코드를 호출자에게 반환하여 오류 코드를 처리하는 등 오류 코드를 호출 체인에 버블링하는 경우 해당 C 코드는 아마도 예외를 사용할 수 있는 후보일 것입니다.이 전환은 사실 사소한 것입니다.간단히throw리턴 코드(C++로 원하는 타입을 모두 던질 수 있음)는 가장 낮은 레벨로 제공됩니다.try{} catch(){}오류를 처리하는 코드의 위치에 있는 문장.할 수 합니다,우 본체를 것을 합니다.main()일순간에try{} catch(){}문장을 작성하고 기록합니다.

이제 뒤로 물러서서 코드를 얼마나 향상시켰는지 확인해 보십시오. 클래스로 변환하지 않고 말입니다.(네, 네, 엄밀히 말하면 구조는 이미 클래스입니다.)하지만 당신은 OOO의 표면을 긁지는 않았지만, 원래의 C 코드를 크게 단순화하고 굳힐 수 있었습니다.

다형성과 상속 그래프가 있는 클래스를 사용하도록 코드를 변환해야 합니까?난 아니라고 한다.C 코드는 OO 모델에 적합한 전체적인 디자인을 가지고 있지 않을 것입니다.위의 각 단계의 목표는 귀하의 C 코드에 OOO 원칙을 주입하는 것과는 무관합니다.목표는 가능한 한 많은 컴파일 시간 제약을 적용하고 코드를 제거하거나 단순화함으로써 기존 코드를 개선하는 것이었습니다.

마지막 단계.

작업이 끝나면 상사에게 보여줄 수 있도록 벤치마크를 추가하는 것을 고려해 보세요.성능 벤치마크 뿐만이 아닙니다.코드 라인, 메모리 사용량, 함수 수 등을 비교합니다.

정말로 7000줄의 코드는 그리 많지 않습니다.이렇게 적은 양의 코드에 대해서는 완전한 재작성이 필요할 수 있습니다.하지만 이 코드는 어떻게 부를까요?아마 전화 거는 사람들이 C API를 기대하고 있을까요?아니면 도서관이 아닌가요?

어쨌든 다시 쓰든 말든, 시작하기 전에 기존 코드에 대해 사람의 개입 없이 쉽게 실행할 수 있는 테스트 세트가 있는지 확인하십시오.그런 다음 변경할 때마다 새 코드에 대한 테스트를 실행합니다.

C++에 대한 이 구두 설명은 자의적인 것 같습니다. 상사에게 왜 그렇게 해야 하는지 물어보고, 같은 목표를 덜 고통스럽게 달성할 수 있는지 알아보고, 덜 고통스러운 새로운 방식으로 하위 집합을 프로토타입화할 수 있는지 확인한 다음 상사에게 시연해 보고 덜 고통스러운 방식을 따르라고 권합니다.

먼저 상사에게 다음과 같은 조치를 취할 때까지 계속하지 않을 것이라고 말합니다.

http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672

그리고 그보다 적은 범위에서:

http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052

둘째, C++ 클래스로 신발 호닝하여 코드를 모듈화하는 방법은 없습니다.이것은 엄청난 일이며 고도의 절차적 코드 리팩토링의 복잡성을 상사에게 전달해야 합니다.

약간의 변화(추출법, 수업이동법 등)를 준 후 테스트하는 것으로 요약됩니다. 이와 관련된 숏컷은 없습니다.

당신의 고통이 느껴지긴 하지만...

여기서 생각하는 것은 모듈화가 증가하면 코드 조각들이 분리되어 미래의 변화가 촉진된다는 것입니다.우리는 하나의 조각이 다른 조각에 영향을 미칠 수 없다는 것을 알기 때문에 그것을 바꿀 자신이 있습니다.

악몽같은 시나리오는 두가지가 있습니다.

  1. 당신은 C 코드를 잘 구성하고 있어 쉽게 C++ 클래스로 변환할 수 있습니다.이 경우 이미 모듈화된 상태일 수도 있고, 유용한 작업을 전혀 수행하지 않았을 수도 있습니다.
  2. 그것은 서로 연결된 것들의 쥐 둥지입니다.그 경우에는 그것을 푸는 것이 매우 어려울 것입니다.모듈화를 높이는 것도 좋지만, 긴 시간이 걸릴 것입니다.

하지만 어쩌면 행복한 매개체가 있을지도 모릅니다.중요하고 개념적으로 고립되어 있지만 데이터 은닉 등의 부족으로 인해 현재 부서지기 쉬운 논리 조각이 있을 수 있습니까? (그렇습니다. 좋은 C는 이 문제로 고통받지 않지만, 그렇지 않으면 우리는 이 문제를 해결할 수 없습니다. 그렇지 않으면 우리는 잘 내버려 둘 것입니다.

그 논리와 데이터를 소유하기 위해 클래스를 끌어내고 그 조각을 둘러싸는 것은 유용할 수 있습니다.C++로 하는 것이 좋을지 C++로 하는 것이 좋을지는 의문입니다. (내 안의 냉소는 "나는 C 프로그래머야, C++는 새로운 것을 배울 수 있는 좋은 기회!"라고 말합니다.)

그래서 저는 이것을 잡아먹어야 할 코끼리로 취급합니다.먼저 그것을 먹어야 하는지 결정하라, 나쁜 코끼리는 그냥 재미가 없다, 잘 구조된 C는 그냥 내버려 둬야 한다.두번째로 적당한 첫물림을 찾습니다.닐이 한 말을 그대로 답하겠습니다. 자동화된 테스트 세트가 없다면 당신은 끝장입니다.

제 생각에는 코드를 완전히 다시 쓰는 것이 더 나은 방법일 것 같은데, 상사에게 어떤 목적으로 오래된 C 코드를 c++ 클래스에 넣기를 원하는지 물어보셔야 합니다.자세한 사항은 문의해 보시기 바랍니다.

분명히 할 수 있습니다. 문제는 비용이 얼마인가요?7K LOC에게도 엄청난 일입니다.당신의 상사는 시간이 많이 걸릴 것이라는 것을 이해하고 있을 것입니다. 하지만 당신은 반짝이는 새로운 기능 등을 작업할 수 없습니다.만약 그가 이 사실을 완전히 이해하지 못했거나 독자 분을 지원할 의사가 없다면, 시작할 이유가 없습니다.

@David가 이미 제안했듯이, Refactoring 책은 필수입니다.

설명을 들어보면 코드의 상당 부분이 이미 "클래스 메소드"인 것처럼 들립니다. 함수가 구조 인스턴스에 대한 포인터를 가져와 해당 인스턴스에서 작동합니다.그래서 C++ 코드로 쉽게 변환할 수 있었습니다.물론, 코드를 더 쉽게 이해하거나 모듈화하는 것을 더 쉽게 만들 수는 없겠지만, 이것이 상사의 주된 바람이라면 그렇게 할 수 있습니다.

또한 리팩토링의 이 부분은 상당히 단순하고 기계적인 프로세스이므로 유닛 테스트 없이도 상당히 안전하게 수행할 수 있습니다(물론 하이퍼어웨어 편집도 가능합니다).그러나 그 이상의 모든 것에 대해서는 단위 테스트가 필요합니다. 변경 사항이 어떤 것도 손상되지 않는지 확인합니다.

이 운동으로 얻을 수 있는 것은 거의 없습니다.좋은 C 코드는 이미 일반적으로 C++보다 모듈화되어 있습니다. 구조체에 대한 포인터를 사용하면 컴파일 단위가 C++에서 pImple이 하는 것과 동일하게 독립적일 수 있습니다. C에서는 구조체의 인터페이스를 노출하기 위해 구조체 내부의 데이터를 노출할 필요가 없습니다.그래서 각각의 C함수를 돌리면,

// Foo.h
typedef struct Foo_s Foo;
int foo_wizz (const Foo* foo, ... );

C++ 클래스에 들어갑니다.

// Foo.hxx
class Foo {
    // struct Foo members copied from Foo.c
    int wizz (... ) const;
};

C 코드와 비교하여 시스템의 모듈화를 줄일 수 있습니다. Foo 유형에 개인 구현 기능이나 멤버 변수가 추가되면 Foo의 모든 클라이언트는 이제 재구축이 필요합니다.

C++의 클래스는 여러 가지를 제공하지만 모듈화는 그 중 하나가 아닙니다.

이 연습을 통해 달성되는 비즈니스 목표가 무엇인지 상사에게 물어보세요.

용어에 대한 참고 사항:

시스템의 모듈은 인터페이스가 잘 정의된 구성 요소로, 시스템의 나머지 부분에 영향을 주지 않고 인터페이스가 동일한 다른 모듈로 교체할 수 있습니다.이러한 모듈로 구성된 시스템은 모듈식입니다.

두 언어 모두 모듈에 대한 인터페이스는 관례적으로 헤더 파일입니다..string.h그리고.stringC 및 C++에서 간단한 문자열 처리 모듈에 대한 인터페이스를 정의하는 것으로서. 새o 이 string.ho됩니다.이 새로운 모듈은 동일한 인터페이스를 가지고 있으며, 이에 동적으로 연결된 모든 모듈은 새로운 구현의 이점을 즉시 얻을 수 있습니다.에 버그가 있는 ,std::string를 다시 ..C++는 시스템에 매우 많은 양의 커플링을 도입하는데, 이 언어는 이를 완화하는 데 아무런 도움이 되지 않습니다. 사실, 그 기능을 완전히 활용하는 C++의 더 나은 사용은 동등한 C 코드보다 훨씬 더 긴밀하게 커플링됩니다.

C++ 모듈화를 시도하면 일반적으로 COM과 같이 모든 개체가 인터페이스(순수한 가상 기본 클래스)와 구현을 모두 가져야 하며 템플릿이 생성된 효율적인 코드를 위해 인다이렉션을 대체하게 됩니다.

시스템이 교체 가능한 모듈로 구성되어 있는지 여부에 상관없이 모듈식으로 만들기 위해 작업을 수행할 필요가 없으며, 적합하게 적용되어 모듈 내에서 응집력을 향상시킬 수 있는 클래스 및 템플릿과 같은 C++의 일부 기능을 사용할 수 있습니다.프로젝트가 정적으로 연결된 단일 애플리케이션을 생성하는 것이라면 모듈식 시스템이 없기 때문에 모듈식성에 전혀 신경 쓰지 않아도 됩니다.템플릿을 사용하여 서로 다른 알고리즘과 데이터 구조를 결합하는 멋진 예인 안티그레인 지오메트리와 같은 것을 만들고 싶다면 C++에서 이를 수행해야 합니다. 널리 퍼져 있는 다른 것만큼 강력한 것은 없습니다.

따라서 관리자가 '모듈화'한다는 것이 무엇을 의미하는지 매우 주의해야 합니다.

을 중 포인터를 하는 할 때 수 한 차이는 포인터를 인 "" "한다"로입니다. 얻을 수 있는 유일한 차이점은 구조체에 대한 포인터를 암시적으로 바꾸는 것입니다.this포인팅합니다.이는 실제로 시스템이 얼마나 모듈화되었는지에 영향을 미치지 않을 것이며, (구조가 헤더가 아닌 C 파일에서만 정의된다면) 모듈화를 감소시킬 것입니다.

"단지" 7,000줄의 C 코드를 사용하면, 현재의 코드를 이해하는 것조차 시도하지 않고 처음부터 코드를 다시 쓰는 것이 더 쉬울 것입니다.

또한 모듈화 및 리팩토링을 수행하거나 지원하는 자동화된 방법은 없습니다.

7000 LOC는 많은 것처럼 들리겠지만 이것의 대부분은 보일러 플레이트가 될 것입니다.

c++로 변경하기 전에 코드를 단순화할 수 있는지 확인해 보세요.기본적으로 그는 단지 함수를 클래스 메소드로 변환하고 구조를 클래스 데이터 멤버로 변환하기를 원하는 것 같습니다(함수 포인터가 없으면 실제 메소드로 변환).이 프로그램의 원본 코더와 연락을 취할 수 있습니까?이해하는 데 도움이 될 수도 있겠지만, 주로 저는 전체의 "엔진"인 코드 조각을 찾고 거기서 새로운 소프트웨어를 기반으로 합니다.또한, 상사는 단순히 전체를 다시 쓰는 것이 좋을 때도 있지만, 기존 프로그램은 실행 시간 동작을 모방하기에 매우 좋은 참고 자료라고 말했습니다.물론 전문화된 알고리즘은 기록하기 어렵습니다.제가 한 가지 장담할 수 있는 것은 이 코드가 최선이 아니라면 나중에 많은 문제를 겪게 될 것이라는 것입니다.당신의 상사에게 가서 프로그램의 처음부터 다시 진행해야 한다는 것을 홍보하겠습니다.방금 다녀왔는데 상사가 다시 쓸 수 있는 능력을 줘서 정말 기쁩니다.이제 2.0 버전은 원래 버전보다 광년 앞섰습니다.

저는 http://www.javaworld.com/javaworld/jw-03-2001/jw-0323-badcode.html?page=7 의 "나쁜 코드를 좋게 만들어라"라는 제목의 이 기사를 읽었습니다. 자바 사용자들을 대상으로 했지만, 제 생각에 그 모든 아이디어는 당신의 경우에 적용될 수 있을 것입니다.제목이 불량 코드만을 위한 것처럼 들리지만, 일반적으로 유지보수 엔지니어를 위한 것이라고 생각합니다.

파렐 박사의 생각을 요약하면 다음과 같습니다.

  1. 쉬운 일부터 시작하세요.
  2. 주석 수정
  3. 서식 수정
  4. 프로젝트 규약을 따릅니다.
  5. 자동화 테스트 쓰기
  6. 대용량 파일/기능을 분리합니다.
  7. 코드를 이해하지 못하는 코드 다시 쓰기

다른 사람들의 조언에 따라 시간이 날 때 읽는 것이 좋을 것 같습니다.

행운을 빕니다.

언급URL : https://stackoverflow.com/questions/3007707/how-to-update-old-c-code

반응형