C ++에서 C로 이동 필드에서 C

몇 년 동안 C ++로 코딩 한 후 최근 임베디드 분야에서 C로 코딩하는 작업을 제안 받았습니다.

임베디드 필드에서 C ++를 무시하는 것이 옳은지 그른지에 대한 질문을 제쳐두고 C ++에는 몇 가지 기능 / 관용구가 있습니다. 몇가지 말하자면:

  • 일반 형식이 안전한 데이터 구조 (템플릿 사용).
  • RAII. 특히 여러 리턴 포인트가있는 함수에서, 예를 들어 각 리턴 포인트에서 뮤텍스를 해제하는 것을 기억할 필요가 없습니다.
  • 일반적으로 소멸자. 즉, MyClass에 대해 d’ tor를 한 번 작성하면 MyClass 인스턴스가 MyOtherClass의 멤버 인 경우 MyOtherClass는 MyClass 인스턴스를 명시 적으로 초기화 할 필요가 없습니다. 해당 d’ tor는 자동으로 호출됩니다.
  • 네임 스페이스.

C ++에서 C로 전환 한 경험은 무엇입니까?
좋아하는 C ++ 기능 / 관용구를 대체하는 C는 무엇입니까? C ++에 원하는 C 기능을 발견 했습니까?



답변

임베디드 프로젝트에서 작업하면서 모든 C로 한 번 작업을 시도했지만 참을 수 없었습니다. 너무 장황해서 아무것도 읽기가 어려웠습니다. 또한 내가 작성한 내장 컨테이너에 최적화 된 것이 마음에 들었습니다.이 컨테이너는 훨씬 덜 안전하고 #define블록 을 수정하기가 더 어렵습니다 .

C ++의 코드는 다음과 같습니다.

if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
    pktQueue.Dequeue(1);

다음으로 바뀝니다.

if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
    Queue_Packet_Dequeue(pktQueue, 1);

많은 사람들이 아마 괜찮다고 말 하겠지만 한 줄에 두 번 이상의 “방법”호출을해야한다면 우스꽝스러워집니다. 두 줄의 C ++는 80 자의 줄 길이 제한으로 인해 C의 다섯 줄로 바뀝니다. 둘 다 동일한 코드를 생성하므로 대상 프로세서가 신경 쓰지 않습니다!

한 번은 (1995 년) 다중 프로세서 데이터 처리 프로그램을 위해 많은 C를 작성하려고했습니다. 각 프로세서에 자체 메모리와 프로그램이있는 종류입니다. 공급 업체에서 제공 한 컴파일러는 C 컴파일러 (일종의 HighC 파생물) 였고 라이브러리는 폐쇄 된 소스 였기 때문에 GCC를 사용하여 빌드 할 수 없었으며 API는 프로그램이 주로 초기화 / 프로세스가 될 것이라는 사고 방식으로 설계되었습니다. / 종결 다양성, 그래서 프로세서 간 통신은 기껏해야 초보적이었습니다.

포기하기 약 한 달 전에 cfront 사본을 찾았습니다. C ++를 사용할 수 있도록 makefile에 해킹했습니다. Cfront는 템플릿도 지원하지 않았지만 C ++ 코드는 훨씬 더 명확했습니다.

일반 형식이 안전한 데이터 구조 (템플릿 사용).

C가 템플릿에 가장 가까운 것은 다음과 같은 많은 코드로 헤더 파일을 선언하는 것입니다.

TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ /* ... */ }

그런 다음 다음과 같이 가져옵니다.

#define TYPE Packet
#include "Queue.h"
#undef TYPE

처음 unsigned char만들지 않는 한 복합 유형 (예 :의 대기열 없음 ) 에는 작동하지 않습니다 typedef.

아, 그리고이 코드가 실제로 어디에도 사용되지 않는다면 구문 상 올바른지조차 알지 못합니다.

편집 : 한 가지 더 : 코드 인스턴스화 를 수동으로 관리 해야 합니다. “템플릿”코드가 모두 인라인 함수 가 아닌 경우 , 링커가 “다중 Foo 인스턴스”오류를 뱉어 내지 않도록 한 번만 인스턴스화되도록 일부 제어를해야합니다. .

이렇게하려면 헤더 파일의 “구현”섹션에 인라인되지 않은 항목을 넣어야합니다.

#ifdef implementation_##TYPE

/* Non-inlines, "static members", global definitions, etc. go here. */

#endif

그런 다음 템플릿 변형 당 모든 코드의 곳에서 다음 을 수행해야합니다.

#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE

또한이 구현 섹션 은 다른 헤더 파일에 템플릿 헤더 파일을 포함 할 수 있지만 나중에 파일 에서 인스턴스화해야하기 때문에 표준 / / litany 외부 에 있어야 합니다.#ifndef#define#endif.c

네, 추악하게 빠르게됩니다. 그래서 대부분의 C 프로그래머는 시도조차하지 않습니다.

RAII.

특히 여러 리턴 포인트가있는 함수에서, 예를 들어 각 리턴 포인트에서 뮤텍스를 해제하는 것을 기억할 필요가 없습니다.

음, 예쁜 코드를 잊고 (함수의 끝 제외) 모든 반환 지점에 익숙해 goto들 :

TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{
    TYPE * result;
    Mutex_Lock(this->lock);
    if(this->head == this->tail)
    {
        result = 0;
        goto Queue_##TYPE##_Top_exit:;
    }

    /* Figure out `result` for real, then fall through to... */

Queue_##TYPE##_Top_exit:
    Mutex_Lock(this->lock);
    return result;
}

일반적으로 소멸자.

즉, MyClass에 대해 d’ tor를 한 번 작성하면 MyClass 인스턴스가 MyOtherClass의 멤버 인 경우 MyOtherClass는 MyClass 인스턴스를 명시 적으로 초기화 할 필요가 없습니다. 해당 d’ tor는 자동으로 호출됩니다.

객체 생성은 동일한 방식으로 명시 적으로 처리되어야합니다.

네임 스페이스.

그것은 실제로 고치기 쉬운 것 입니다. 모든 심볼에 접두사를 붙이기 만하면 됩니다 . 이것이 제가 이전에 이야기했던 소스 팽창의 주요 원인입니다 (클래스는 암시 적 네임 스페이스이기 때문입니다). C 사람들은 이것을 영원히 살았고 아마도 큰 문제가 무엇인지 알지 못할 것입니다.

YMMV


답변

나는 다른 이유로 (알레르기 반응의 일종) C ++에서 C로 옮겼고, 내가 놓친 것은 몇 가지 뿐이고 얻은 것들은 몇 가지뿐입니다. C99를 고수한다면, 가능하다면 아주 멋지고 안전하게 프로그래밍 할 수있는 구조가 있습니다. 특히

  • 지정된 이니셜 라이저 (결국 매크로와 결합 됨)는 간단한 클래스를 생성자처럼 쉽게 초기화합니다.
  • 임시 변수에 대한 복합 리터럴
  • for-scope 변수는 범위 바운드 리소스 관리 를 수행하는 데 도움이 될 수 있습니다 . 특히 unlock뮤텍스 또는free 예비 함수 반환 하에서도 배열 합니다.
  • __VA_ARGS__ 매크로를 사용하여 함수에 대한 기본 인수를 갖고 코드 언 롤링을 수행 할 수 있습니다.
  • inline (일종의) 오버로드 된 함수를 대체하기 위해 잘 결합 된 함수 및 매크로


답변

C에는 STL과 같은 것이 없습니다.
유사한 기능을 제공하는 라이브러리가 있지만 더 이상 내장되어 있지 않습니다.

이것이 내 가장 큰 문제 중 하나라고 생각합니다. 어떤 도구로 문제를 해결할 수 있는지 알고 있지만 사용해야하는 언어로 도구를 사용할 수 없습니다.


답변

C와 C ++의 차이점은 코드 동작의 예측 가능성입니다.

C에서 코드가 수행 할 작업을 매우 정확하게 예측하는 것이 더 쉬우 며, C ++에서는 정확한 예측을내는 것이 조금 더 어려울 수 있습니다.

C의 예측 가능성은 코드가 수행하는 작업을 더 잘 제어 할 수있게 해주지 만 더 많은 작업을 수행해야 함을 의미합니다.

C ++에서는 동일한 작업을 수행하기 위해 더 적은 코드를 작성할 수 있지만 (나를 위해) 가끔 객체 코드가 메모리에 배치되는 방식과 예상되는 동작을 아는 데 어려움이 있습니다.


답변

그건 그렇고, 내 작업 라인에서 나는 C와 C ++ 사이를 끊임없이 앞뒤로 전환하고 있습니다.

C에있을 때 C ++에서 그리워합니다.

  • 템플릿 (STL 컨테이너를 포함하되 이에 국한되지 않음). 특수 카운터, 버퍼 풀 등과 같은 작업에 사용합니다 (다른 임베디드 프로젝트에서 사용하는 클래스 템플릿 및 함수 템플릿 라이브러리를 구축했습니다).

  • 매우 강력한 표준 라이브러리

  • 물론 RAII를 가능하게하는 소멸자 (뮤텍스, 인터럽트 비활성화, 추적 등)

  • 액세스 지정자를 사용하여 누가 무엇을 사용할 수 있는지 (볼 수 없음)

나는 더 큰 프로젝트에서 상속을 사용하고, C ++의 내장 지원은 기본 클래스를 첫 번째 멤버로 포함하는 C “해킹”보다 훨씬 깨끗하고 멋집니다 (생성자, 초기화 목록 등의 자동 호출은 말할 것도 없습니다. ) 그러나 위에 나열된 항목은 내가 가장 그리워하는 항목입니다.

또한 사용 예외에 대해 작업하는 임베디드 C ++ 프로젝트의 1/3 정도에 불과하므로 예외없이 생활하는 데 익숙해 져서 C로 돌아갈 때 너무 그리워하지 않습니다.

반대로 상당수의 개발자가 참여하는 C 프로젝트로 돌아 가면 사라지는 사람들에게 설명하는 데 익숙한 전체 클래스의 C ++ 문제가 있습니다. 대부분 C ++의 복잡성으로 인한 문제와 무슨 일이 일어나고 있는지 알고 있다고 생각하지만 실제로는 C ++ 신뢰 곡선 의 “C with classes”부분에 있습니다. 있습니다.

선택권이 주어지면 프로젝트에서 C ++를 사용하는 것을 선호하지만 팀이 언어에 대해 상당히 견실 한 경우에만 가능합니다. 물론 제가 “C”를 효과적으로 작성하고있는 8K μC 프로젝트가 아니라고 가정합니다.


답변

몇 가지 관찰

  • C ++ 컴파일러를 사용하여 C를 빌드 할 계획이 아니라면 (C ++의 잘 정의 된 하위 집합을 고수하는 경우 가능함) 곧 컴파일러가 C에서 허용하는 C ++의 컴파일 오류를 발견하게됩니다.
  • 더 이상 모호한 템플릿 오류가 없습니다 (예!)
  • (지원되는 언어) 객체 지향 프로그래밍 없음


답변

순수한 C가 아닌 C ++ 또는 C / C ++ 혼합을 사용하는 것과 거의 동일한 이유입니다. 네임 스페이스 없이도 살 수 있지만 코드 표준에서 허용하는 경우 항상 사용합니다. 그 이유는 C ++로 훨씬 더 간결한 코드를 작성할 수 있기 때문입니다. 이것은 나에게 매우 유용합니다. 저는 가끔 충돌하는 경향이있는 C ++로 서버를 작성합니다. 이 시점에서보고있는 코드가 짧고 구성되어 있으면 많은 도움이됩니다. 예를 들어 다음 코드를 고려하십시오.

uint32_t
ScoreList::FindHighScore(
  uint32_t p_PlayerId)
{
  MutexLock lock(m_Lock);

  uint32_t highScore = 0;
  for(int i = 0; i < m_Players.Size(); i++)
  {
    Player& player = m_Players[i];
    if(player.m_Score > highScore)
      highScore = player.m_Score;
  }

  return highScore;
}

C에서 다음과 같이 보입니다.

uint32_t
ScoreList_getHighScore(
  ScoreList* p_ScoreList)
{
  uint32_t highScore = 0;

  Mutex_Lock(p_ScoreList->m_Lock);

  for(int i = 0; i < Array_GetSize(p_ScoreList->m_Players); i++)
  {
    Player* player = p_ScoreList->m_Players[i];
    if(player->m_Score > highScore)
      highScore = player->m_Score;
  }

  Mutex_UnLock(p_ScoreList->m_Lock);

  return highScore;
}

차이의 세계가 아닙니다. 한 줄의 코드가 더해 지지만 그것은 합쳐지는 경향이 있습니다. 일반적으로 깨끗하고 가늘게 유지하기 위해 최선을 다하지만 때로는 더 복잡한 작업을해야합니다. 그리고 그러한 상황에서 당신은 당신의 라인 수를 중요하게 생각합니다. 한 줄 더는 브로드 캐스트 네트워크가 갑자기 메시지 전달을 중단하는 이유를 알아 내려고 할 때 살펴 봐야 할 또 하나입니다.

어쨌든 C ++를 사용하면 안전한 방식으로 더 복잡한 작업을 수행 할 수 있습니다.


이 글은 C++ 카테고리에 분류되었고 , 태그가 있으며 님에 의해 에 작성되었습니다.