나는 C와 C ++ 코드를 혼용해서는 안된다고 생각한다. 이것이 문제이며 해결하는 방법입니까?


10

배경 / 시나리오

나는 순전히 C로 CLI 애플리케이션을 작성하기 시작했다. 중간 쯤에 사용자 입력 (문자 배열)의 "문자열"을 사용하고 있었고 C ++ 문자열 스 트리머 객체를 발견했습니다. 이것들을 사용하여 코드를 저장할 수 있다는 것을 알았으므로 응용 프로그램을 통해 사용했습니다. 이것은 파일 확장자를 .cpp로 변경하고 이제 g++대신을 사용하여 앱을 컴파일한다는 것을 의미합니다 gcc. 따라서이를 바탕으로 응용 프로그램은 기술적으로 C ++ 응용 프로그램이라고 말합니다 (제한된 경험으로 인해 두 언어 사이에 많은 크로스 오버가 있기 때문에 코드의 90 % 이상이 C라고 부르는 것으로 작성되었지만) 두). 약 900 줄 길이의 단일 .cpp 파일입니다.

중요한 요소

나는 프로그램이 (돈으로) 자유롭고 자유롭게 배포되고 모든 사람이 사용할 수 있기를 원합니다. 내 관심사는 누군가 코드를 살펴보고 다음과 같은 효과가 있다고 생각합니다.

오 코딩을 봐, 끔찍하다,이 프로그램은 나를 도울 수 없어

잠재적으로 가능할 때! 또 다른 문제는 효율적인 코드입니다 (이더넷 연결을 테스트하기위한 프로그램입니다). 비효율적 인 코드 부분은 응용 프로그램 또는 출력 성능을 심각하게 방해 할 수있는 부분이 없어야합니다. 그러나 특정 함수, 메소드, 객체 호출 등에 대한 도움을 요청할 때 이것이 스택 오버플로에 대한 질문이라고 생각합니다.

내 질문

(내 의견으로는) C와 C ++을 혼합해서는 안됩니다. C ++에서 모두 다시 작성하려고하면 (이를 통해 더 새로운 C ++ 기술을 사용하여 압축 할 수있는 C 스타일로 무언가를 코딩 한 더 많은 C ++ 객체 및 메소드를 구현하거나 문자열 스 트리머 객체 및 C 코드에 "다시"가져 오시겠습니까? 여기에 올바른 접근법이 있습니까? 나는이 응용 프로그램을 대중의 눈에 "좋은"상태로 유지하는 방법에 대한 지침을 잃어 버렸습니다.

코드-업데이트

다음 은 코드에 대한 링크입니다. 40 % 정도의 의견이며, 나는 더 유창하다고 느낄 때까지 거의 모든 줄을 언급합니다. 내가 링크 한 사본에서 거의 모든 의견을 제거했습니다. 나는 이것이 읽기가 너무 어렵지 않기를 바랍니다. 아무도 그것을 완전히 이해할 필요는 없지만 희망합니다. 그러나 치명적인 디자인 결함을 만든 경우 쉽게 식별 할 수 있기를 바랍니다. 나는 또한 두 개의 우분투 데스크탑과 랩톱을 쓰고 있다고 언급해야합니다. 코드를 다른 운영 체제로 이식하려고하지 않습니다.


3
나는 당신을 위해 CLI를 명확히했다. CLI는 C 관점에서는 그다지 의미가없는 공용 언어 인프라를 참조 할 수도 있습니다.
Robert Harvey

FORTRAN으로 다시 작성할 수 있습니다. OOF에 대해 들어 본 적이 없습니다.
ott--

2
"예쁘지 않은"소프트웨어를 사용하지 않는 사람들에 대해 걱정하지 않아도됩니다. 사용자의 99 %는 코드를 보거나 필요한 작업을 수행하는 한 코드 작성 방식에 신경 쓰지 않습니다. 그러나 장기적으로 유지 관리하는 데 도움 되도록 코드가 일관성이 있어야합니다 .
Evicatos 2018 년

1
github 와 같은 코드 를 사용하여 무료 소프트웨어 (예 : GPLv3 라이센스)를 작성하지 않습니까 ? 코드에 LICENSE파일이 없습니다. 흥미로운 피드백을받을 수 있습니다.
Basile Starynkevitch 2016 년

답변:


13

처음부터 시작합시다. 혼합 C 코드와 C ++ 코드는 상당히 일반적입니다. 그래서 당신은 시작하기 위해 큰 클럽에 있습니다. 우리는 야생에 거대한 C 코드베이스를 가지고 있습니다. 그러나 명백한 이유로 많은 프로그래머들은 적어도 C에서 적어도 새로운 것을 작성하지 않고 동일한 컴파일러에서 C ++에 액세스하면 새로운 모듈이 그러한 방식으로 작성되기 시작합니다. 처음에는 기존 부분 만 남겨 둡니다.

그런 다음 일부 기존 파일이 C ++로 다시 컴파일되고 일부 브리지가 삭제 될 수 있습니다. 그러나 시간이 오래 걸릴 수 있습니다.

당신은 다소 앞서 가고 있습니다. 당신의 전체 시스템은 이제 C ++입니다. 대부분의 시스템은 "C- 스타일"입니다. 그리고 당신은 스타일의 혼합에 문제가있는 것을 보지 말아야합니다 : C ++는 많은 스타일을 지원하는 다중 패러다임 언어이며, 그것들이 서로 공존 할 수 있도록합니다. 실제로 그것은 주된 강점이며, 당신은 단일 스타일을 강요받지 않습니다. 어느 곳에서나 운이없는 이곳 저곳에 적합하지 않은 것.

코드베이스가 다시 작동하면 코드베이스를 다시 작업하는 것이 좋습니다. 또는 개발에 방해가되는 경우. 그러나 (원래의 의미로) 작동하는 경우 가장 기본적인 엔지니어링 원칙을 따르십시오. 파산되지 않은 경우 수정하지 마십시오. 차가운 부분 만 내버려두고 계산에 집중하십시오. 나쁘거나 위험하거나 새로운 기능을 갖춘 부품에 부품을 리팩터링하여 부품을 침대로 만듭니다.

해결해야 할 일반적인 사항을 찾으려면 C 코드베이스에서 제거 할 가치가 있습니다.

  • 모든 str * 함수와 char []-문자열 클래스로 대체
  • sprintf를 사용하는 경우 결과와 함께 문자열을 반환하거나 문자열에 넣고 버전을 바꾸는 버전을 만듭니다. (당신이 스트림을 신경 쓰지 않는다면 당신이 좋아하지 않는 한 스스로 선호하고 건너 뛰십시오. gcc는 형식을 확인하기 위해 완벽한 유형 안전성을 제공합니다. 적절한 속성을 추가하십시오.
  • 대부분의 malloc 및 무료-신규 및 삭제가 아니라 벡터, 목록,지도 및 기타 수집가와 관련이 없습니다.
  • 나머지 메모리 관리 (앞의 두 지점 이후에는 매우 드 물어야합니다. 스마트 포인터로 가리거나 특별한 컬렉션을 구현하십시오)
  • RAII 랩퍼 또는 클래스를 사용하도록 다른 모든 자원 사용량 (FILE *, mutex, lock 등)을 대체하십시오.

완료되면 코드베이스가 예외적으로 안전 할 수있는 지점에 접근하므로 예외를 사용하여 리턴 코드 풋볼을 삭제하고 고급 기능에서만 드문 시도 / 잡기를 할 수 있습니다.

그 외에도 건강한 C ++로 새로운 코드를 작성하고 기존 코드를 대체 할 수있는 클래스가 태어났다면 선택하십시오.

구문 관련 내용은 언급하지 않았으며 모든 새 코드에서 포인터 대신 참조를 사용하지만 해당 변경 사항으로 이전 C 부분을 대체하는 것은 좋은 가치가 아닙니다. 해결해야 할 캐스트는 가능한 모든 것을 제거하고 나머지에 대한 랩퍼 함수에서 C ++ 변형을 사용해야합니다. 그리고 매우 중요한 것은 const를 해당하는 곳에 추가하십시오. 이들은 이전 글 머리 기호와 인터리브됩니다. 매크로를 통합하고 열거 형, 인라인 함수 또는 템플릿으로 만들 수있는 것을 대체하십시오.

Sutter / Alexandrescu의 C ++ 코딩 표준을 아직 읽지 않았다면 읽어 보시고 잘 따르십시오.


입력 Balog, 내 눈에 모든 건전한 조언에 대해 많은 감사와 나는 그것에 동의합니다. 작업 코드의 우선 순위를 결정하면서 섹션별로 코드 섹션을 천천히 변경하는 방법을 살펴 보겠습니다.
jwbensley 2016 년

7

간단히 말해서, 당신은 지옥에 가지 않을 것이고, 나쁜 일은 일어나지 않을 것입니다.

C ++을 "더 나은 C"로 사용하는 것이 가능하지만 C ++의 많은 이점을 버리고 있지만 바닐라 C로 제한하지 않기 때문에 C의 이점 (단순성, 이식성)을 얻지 못합니다 투명성, 상호 운용성). 다시 말해, C ++는 C의 일부 특성을 희생하여 다른 특성을 얻습니다. 예를 들어, 메모리 할당이 언제 어디서 언제 발생하는지 명확하게 볼 수있는 C의 투명성은 C ++의보다 강력한 추상화로 교환됩니다.

코드가 이제는 제대로 작동하는 것 같으므로 코드를 다시 작성하는 것이 좋습니다. 지금은 그대로 유지하고 한 번에 하나씩 더 관용적 인 C ++로 변경하십시오. 주어진 부분에서 작업 할 때마다 그리고 다음 프로젝트를 위해 배운 교훈을 명심하십시오.이 모든 것 중 가장 중요한 것은 C와 C ++는 같은 언어가 아니며 프로젝트 중간에 C ++로 전환하기로 결정하는 것보다 먼저 선택하는 것이 좋습니다. .


조언을 해주셔서 감사합니다. 나는 당신이하는 말에 동의하며, 그것을 승선시키고 있습니다. 감사합니다!
jwbensley 2016 년

4

C ++는 많은 기능을 가지고 있다는 점에서 매우 복잡한 언어입니다. 또한 여러 패러다임을 지원하는 언어이므로 객체를 사용하지 않고 전체 절차 코드를 작성할 수 있습니다.

따라서 C ++은 매우 복잡하기 때문에 사람들이 코드에서 일부 제한된 기능을 사용하는 경우가 종종 있습니다. 초보자는 종종 malloc / free 대신 스트림 I / O, 문자열 객체 및 new / delete를 사용하고 포인터 대신 참조를 사용합니다. 객체 지향 기능에 대해 배우면서 "C with classes"라는 스타일로 글을 시작할 수 있습니다. 결국 C ++에 대해 더 많이 배우면 템플릿, RAII , STL , 스마트 포인터 등을 사용하기 시작합니다 .

내가하려는 것은 C ++을 배우는 것이 시간이 걸리는 과정이라는 것입니다. 예, 지금 코드는 C 프로그래머가 C ++을 작성하려고 시도한 것 같습니다. 그리고 당신은 단지 배우기 때문에 완벽하게 괜찮습니다. 그러나 더 잘 알아야 할 노련한 프로그래머가 작성한 프로덕션 코드에서 이런 종류의 것을 볼 때 나를 울리게합니다.

좋은 포트란 프로그래머는 모든 언어로 좋은 포트란 코드를 작성할 수 있습니다. :)


2

C ++로 갈 때 많은 오래된 C 프로그래머가했던 길을 정확하게 되풀이하는 것 같습니다. "더 나은 C"로 사용하고 있습니다. 20 년 전 이것은 의미가 있었지만이 시점에서 C ++에는 표준 템플릿 라이브러리와 같은 훨씬 더 강력한 구성이있어 거의 이해가되지 않습니다. 코드를 볼 때 C ++ 프로그래머에게 동맥류를주지 않도록 최소한 다음을 수행해야합니다.

  • ? 대신 스트림을 사용하십시오. printf가족.
  • new대신에 사용하십시오 malloc.
  • 모든 데이터 구조에 STL 컨테이너 클래스를 사용하십시오.
  • 사용 std::string하는 대신 char*가능한
  • RAII 이해 및 사용

프로그램이 짧고 (약 900 줄이 짧아 보인다면) 개인적으로 강력한 클래스 세트를 만들려고하는 것이 필요하거나 유용하다고 생각하지 않습니다.


조언 스티븐에게 감사합니다. 예 클래스에 대해 같은 생각을 가지고 있었고 코드를 깔끔한 단일 파일로 정리할 수 있다고 생각합니다. 감사!
jwbensley 2016 년

2

받아 들여진 대답은 C ++ 코드가 C 코드보다 절대적으로 더 나은 것처럼 C를 관용 C ++로 변환하는 전문가 만 언급합니다. 혼합 코드가 손상되지 않은 경우 급격한 변경을 수행해야 할 필요가 있다는 다른 답변에 동의합니다.

C와 C ++의 혼합이 지속 가능한지 여부는 혼합이 수행되는 방법에 따라 다릅니다. 예를 들어 C 코드 (예외 안전하지 않음)에서 예외가 발생하여 메모리 누수 또는 데이터 손상이 발생할 때 미묘한 버그가 발생할 수 있습니다. 보다 안전하고 일반적인 방법은 C 라이브러리 또는 인터페이스를 클래스로 랩핑하거나 C ++ 프로젝트에서 다른 종류의 격리에 사용하는 것입니다.

전체 프로젝트를 관용적 C ++ (또는 C)로 다시 작성하기로 결정하기 전에 다른 답변에 제시된 많은 변경 사항으로 인해 프로그램 속도가 느려지거나 원치 않는 다른 효과가 발생할 수 있습니다. 예를 들면 다음과 같습니다.

  • 스택 할당 C- 문자열을 std :: string로 변경하면 불필요한 힙 할당이 발생할 수 있습니다.
  • 원시 포인터를 std :: shared_ptr과 같은 일부 공유 포인터 유형으로 변경하면 참조 횟수, 내부 가상 멤버 함수 및 스레드 안전성으로 인해 액세스 오버 헤드가 발생합니다.
  • 표준 라이브러리 스트림이 C 스트림보다 느립니다.
  • 컨테이너와 같이 RAII 클래스를 부주의하게 사용하면 특히 C ++ 11의 이동 의미론을 사용할 수없는 경우 불필요한 조작이 발생할 수 있습니다.
  • 템플릿을 사용하면 컴파일 시간이 길어지고 컴파일 오류가 명확하지 않으며 컴파일러 간의 코드 팽창 및 이식성 문제가 발생할 수 있습니다.
  • 예외 안전 코드 작성이 어렵 기 때문에 변환 중에 미묘한 버그를 쉽게 도입 할 수 있습니다.
  • 일반 C보다 공유 라이브러리의 C ++ 코드를 사용하는 것이 까다 롭습니다.
  • C ++는 C89와 같이 널리 지원되지 않습니다.

결론적으로, 혼합 된 C 및 C ++ 코드에서 관용적 C ++로 변환하는 것은 구현 시간보다 훨씬 많은 이점이 있으며, 편리한 기능으로 도구 상자를 확장하는 트레이드 오프로 간주해야합니다. 따라서 "종속적"이 아닌 일반적인 경우에 대한 답변을 제공하기가 매우 어렵습니다.


"표준 라이브러리 스트림은 C 스트림보다 느리다": 아마도 국제화가 printf보다 국제화가 어려울 것입니다.
중복 제거기

1

C와 C ++를 혼합하는 것은 좋지 않습니다. 그것들은 별개의 언어이며 실제로 그렇게 취급해야합니다. 가장 편한 것을 골라 그 언어로 관용적 코드를 작성해보십시오.

C ++가 많은 코드를 절약하고 있다면 C ++를 사용하여 C 부분을 다시 작성하십시오. 그것이 당신이 염려한다면 성능 차이가 눈에 띄지 않을 것입니다.


그래서 저는 C로 작성된이 라이브러리를 사용하고 C ++로 작성된 다른 라이브러리를 가지고 있습니다 ... 제대로 작동하는 코드를 다시 작성하는 것은 불필요한 작업을 만드는 것입니다.
gnasher729

1

나는 항상 C와 C ++ 사이를 오가며 이런 식으로 일하는 것을 선호하는 이상한 유형입니다. 내가 말, X 선 데이터 유형에 저를 허용하고 비트처럼 그들을 치료와 함께 바이트 둔기로 C를 사용하는 동안 높은 수준의 것들에 대한 C ++를 선호하는 memcpy나는 낮은 수준의 데이터 구조 및 메모리 할당자를 구현하기위한 알아두면 편리 . 원시 비트와 바이트 수준에서 작업하고 있다면 C ++의 풍부한 유형 시스템은 전혀 도움이되지 않으며 종종 C로 그러한 코드를 작성하는 것이 더 쉽다는 것을 알 수 있습니다. 모든 C 데이터 유형을 비트와 바이트로 취급하십시오.

명심해야 할 것은이 두 언어가 잘 맞지 않는 것입니다.

1. AC 함수는 C ++ 함수를 호출 할 수 없습니다 throw. 일상적인 C ++ 코드가 암시 적으로 예외를 일으킬 수있는 장소가 몇 개인 경우, 일반적으로 C 함수는 일반적으로 C ++ 함수를 호출하지 않아야합니다. 그렇지 않으면 C 코드는 C ++ 예외를 잡는 실용적인 방법이 없기 때문에 스택 해제 중에 할당 된 리소스를 해제 할 수 없습니다. 예를 들어, C 코드가 C ++ 함수를 콜백으로 호출하도록 요구하는 경우 C ++ 측은 C 코드로 돌아 가기 전에 발생한 예외를 포착해야합니다.

2. 데이터 유형을 원시 비트와 바이트로 취급하는 C 코드를 작성하고 유형 시스템에 대한 불도저 (실제로 C를 처음 사용하는 주된 이유) 인 경우 C ++ 데이터에 대해 이러한 코드를 사용하고 싶지 않습니다. 복사 생성자와 소멸자, 가상 포인터 및 그러한 종류의 것들을 가질 수있는 유형. 따라서 일반적으로 이러한 종류의 C 코드를 사용하는 C ++ 코드가 아니라 이러한 종류의 C 코드를 사용하는 C 코드 여야합니다.

C ++ 코드가 그러한 C 코드를 사용하도록하려면 일반적으로 C의 일반적인 C 데이터 구조에 저장하는 데이터가 구성하기가 쉬운 데이터 유형인지 확인하는 클래스의 구현 세부 사항으로 사용하려고합니다 파괴한다. 다행히도 정적 assert에서이를 확인하는 데 사용할 수있는 이와 같은 유형 특성 있으며, 일반 C 데이터 구조에 저장하는 유형이 사소한 소멸자 및 생성자를 가지고 있고 그런 식으로 유지되도록합니다.

C ++에서 모두 다시 작성하려고하면 (이를 통해 더 새로운 C ++ 기술을 사용하여 압축 할 수있는 C 스타일로 무언가를 코딩 한 더 많은 C ++ 객체 및 메소드를 구현하거나 문자열 스 트리머 객체 및 C 코드에 "다시"가져 오시겠습니까?

내 의견으로는 위의 규칙을 준수하고 작성한 테스트에 대해 코드가 잘 테스트되었는지 귀찮게 할 필요가 없습니다. 개선 할 가치가있는 부분은 지금까지 C ++로 작성한 코드이지만, 반드시 C 기반 코드를 C ++로 이식해야한다는 의미는 아닙니다.

C ++로 작성하고 C ++ 코드로 컴파일 한 코드를 사용하면주의해야합니다. C ++에서 C와 같은 코딩을 사용하면 문제가 발생합니다. 예를 들어, 리소스를 수동으로 할당하고 해제하면 코드에서 예외가 발생 free하여 리소스를 사용 하기 전에 함수에서 암시 적으로 종료 될 수 있으므로 코드가 예외 안전하지 않을 가능성이 있습니다. C ++에서 RAII 및 스마트 포인터와 같은 것은 예외적 인 경로를 고려하지 않고 정상적인 실행 경로 만 보았을 때 나타날 수 있기 때문에 푹신한 편의는 아닙니다. 그들은 예외를 만났을 때 모든 곳에서 누출이 시작되지 않는 올바른 예외 안전 코드를 간단하고 효과적으로 작성해야하는 근본적인 필요성이 종종 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.