C ++ 클래스 메모리 구조에서 "스페이서"를 어떻게 만듭니 까?


94

문제

A의 낮은 수준의 베어 메탈 (bare-metal) 임베디드 환경, 나는 액세스 메모리 위치에 사용자를 금지하기 위해, C ++ 구조 내에서 어떤 이름없이, 메모리에 빈 공간을 만들고 싶습니다.

지금 uint32_t :96;은 세 단어를 편리하게 대신 할 추악한 비트 필드를 배치하여 달성 했지만 GCC (uint32_t에 맞기에는 비트 필드가 너무 큼) 경고가 발생합니다. 이는 매우 합법적입니다.

잘 작동하지만 수백 개의 경고가있는 라이브러리를 배포하려는 경우에는 그다지 깨끗하지 않습니다.

어떻게 제대로 수행합니까?

애초에 문제가있는 이유는 무엇입니까?

제가 작업중인 프로젝트는 전체 마이크로 컨트롤러 라인 (STMicroelectronics STM32)의 다양한 주변 장치의 메모리 구조를 정의하는 것으로 구성됩니다. 이를 위해 결과는 대상 마이크로 컨트롤러에 따라 모든 레지스터를 정의하는 여러 구조의 결합을 포함하는 클래스입니다.

매우 간단한 주변기기의 간단한 예는 다음과 같습니다. GPIO (범용 입 / 출력)

union
{

    struct
    {
        GPIO_MAP0_MODER;
        GPIO_MAP0_OTYPER;
        GPIO_MAP0_OSPEEDR;
        GPIO_MAP0_PUPDR;
        GPIO_MAP0_IDR;
        GPIO_MAP0_ODR;
        GPIO_MAP0_BSRR;
        GPIO_MAP0_LCKR;
        GPIO_MAP0_AFR;
        GPIO_MAP0_BRR;
        GPIO_MAP0_ASCR;
    };
    struct
    {
        GPIO_MAP1_CRL;
        GPIO_MAP1_CRH;
        GPIO_MAP1_IDR;
        GPIO_MAP1_ODR;
        GPIO_MAP1_BSRR;
        GPIO_MAP1_BRR;
        GPIO_MAP1_LCKR;
        uint32_t :32;
        GPIO_MAP1_AFRL;
        GPIO_MAP1_AFRH;
        uint32_t :64;
    };
    struct
    {
        uint32_t :192;
        GPIO_MAP2_BSRRL;
        GPIO_MAP2_BSRRH;
        uint32_t :160;
    };
};

모두 GPIO_MAPx_YYY가 매크로이며 uint32_t :32또는 레지스터 유형 (전용 구조) 으로 정의됩니다 .

여기에서 uint32_t :192;잘 작동 하는 것을 볼 수 있지만 경고가 발생합니다.

지금까지 고려한 사항 :

나는 그것을 여러 개 uint32_t :32;(여기서는 6 개) 로 대체했을 수도 있지만, 내가 가진 극단적 인 경우 uint32_t :1344;(42 개)가 있습니다. 따라서 구조 생성이 스크립팅 되었음에도 불구하고 8k 다른 것 위에 약 100 줄을 추가하지 않을 것입니다.

정확한 경고 메시지는 다음과 같습니다. width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type(저는 그것이 얼마나 그늘진 지 좋아합니다).

차라리 것 없는 단순히 경고를 제거하여이 문제를 해결하지만, 사용

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop

내가 발견하면 ... 해결책이 될 수 있습니다 TheRightFlag. 그러나, 지적 이 스레드 , gcc/cp/class.c이 슬픈 코드 부분 :

warning_at (DECL_SOURCE_LOCATION (field), 0,
        "width of %qD exceeds its type", field);

-Wxxx이 경고를 제거 할 플래그 가 없음을 알려줍니다 .


26
등을 고려 char unused[12];했습니까?
MM

3
나는 경고를 억제 할 것입니다. [class.bit] / 1uint32_t :192;.
NathanOliver

3
@NathanOliver 나도 기꺼이 할 것이지만이 경고는 억제 할 수없는 것 같거나 (GCC 사용) 그렇게하는 방법을 찾지 못한 것 같습니다. 더욱이, 여전히 깨끗한 방법은 아닙니다 (하지만 꽤 만족 스러울 것입니다). 올바른 "-W"플래그를 찾았지만 내 파일에만 적용하지 못했습니다 (사용자가 자신의 작업에 대해 이러한 종류의 경고를 제거하는 것을 원하지 않습니다).
J Faucher

3
:42*32대신 쓸 수있는 BTW:1344
MM

1
경고를 억제하려면 이것을 시도 하시겠습니까? gcc.gnu.org/onlinedocs/gcc/…
Hitobat

답변:


36

인접한 여러 개의 익명 비트 필드를 사용하십시오. 그래서 대신 :

    uint32_t :160;

예를 들어 다음과 같습니다.

    uint32_t :32;
    uint32_t :32;
    uint32_t :32;
    uint32_t :32;
    uint32_t :32;

익명을 원하는 각 레지스터마다 하나씩.

채울 공간이 큰 경우 더 명확하고 단일 32 비트 공간을 반복하기 위해 매크로를 사용하는 오류가 적을 수 있습니다. 예를 들면 다음과 같습니다.

#define REPEAT_2(a) a a
#define REPEAT_4(a) REPEAT_2(a) REPEAT_2(a)
#define REPEAT_8(a) REPEAT_4(a) REPEAT_4(a)
#define REPEAT_16(a) REPEAT_8(a) REPEAT_8(a)
#define REPEAT_32(a) REPEAT_16(a) REPEAT_16(a)

그런 다음 1344 (42 * 32 비트) 공간을 추가 할 수 있습니다.

struct
{
    ...
    REPEAT_32(uint32_t :32;) 
    REPEAT_8(uint32_t :32;) 
    REPEAT_2(uint32_t :32;)
    ...
};

답변 해주셔서 감사합니다. 나는 이미, 그러나 그것은 내 파일 (의 일부에 200 선을 추가 할 것을 고려 uint32_t :1344;차라리 이런 식으로 갈 필요가 없습니다 것입니다 ... 그래서 장소에)
J FAUCHER을

1
@JFaucher 라인 카운트 요구 사항에 가능한 솔루션을 추가했습니다. 이러한 요구 사항이있는 경우 해당 요구 사항을 충족하지 않는 답변을 얻지 않도록 질문에 언급 할 수 있습니다.
Clifford

편집에 감사 드리며 줄 수를 언급하지 않은 것에 대해 죄송합니다. 내 요점은 많은 줄이 있기 때문에 코드가 이미 고통스럽고 너무 많이 추가하는 것을 피하고 싶다는 것입니다. 따라서 나는 누군가가 인접 익명 비트 필드를 사용하지 않는 "깨끗한"또는 "공식적인"방법을 알고 있는지 묻고있었습니다. 그래도 매크로 접근 방식은 괜찮아 보입니다. 그건 그렇고, 귀하의 예에서 36 * 32 비트 공간이 없습니까?
J Faucher

@JFaucher-수정되었습니다. I / O 레지스터 매핑 파일은 레지스터 수가 많기 때문에 반드시 커야합니다. 일반적으로 한 번 작성하고 하드웨어가 상수이기 때문에 유지 관리는 문제가되지 않습니다. "숨기기"레지스터를 제외하고 나중에 액세스해야하는 경우 직접 유지 관리 작업을 수행합니다. 물론 모든 STM32 장치에는 공급 업체에서 제공하는 레지스터 맵 헤더가 이미 있다는 것을 알고 있습니까? 그것을 사용하면 오류가 훨씬 적습니다.
Clifford

2
나는 귀하의 의견에 동의하며 공정하게 귀하의 답변에 표시된 두 가지 방법 중 하나를 사용할 것이라고 생각합니다. C ++이 더 나은 솔루션을 제공하지 않는지 확인하고 싶었습니다. 나는 ST가 이러한 헤더를 제공한다는 것을 잘 알고 있지만, 그것들은 매크로와 비트 연산의 방대한 사용을 기반으로 구축되었습니다. 내 프로젝트는 오류 가 발생 하는 헤더에 해당하는 C ++를 빌드하는 것입니다 (열거 형 클래스, 비트 필드 등 사용). 이것이 우리가 CMSIS 헤더를 C ++ 구조로 "번역"하는 스크립트를 사용하는 이유입니다 (그리고 ST 파일 btw에서 일부 오류를 발견했습니다)
J Faucher 2018

45

C ++ 방식은 어떻습니까?

namespace GPIO {

static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);

}

int main() {
    GPIO::MAP0_MODER = 42;
}

GPIO네임 스페이스로 인해 자동 완성이 이루어 지며 더미 패딩이 필요하지 않습니다. 심지어 각 레지스터의 주소를 볼 수 있으므로 무슨 일이 일어나고 있는지 더 명확합니다. 컴파일러의 패딩 동작에 전혀 의존 할 필요가 없습니다.


1
이는 동일한 함수에서 여러 MMIO 레지스터에 액세스하기 위해 구조체보다 덜 최적화 할 수 있습니다. 레지스터의 기본 주소에 대한 포인터를 사용하면 컴파일러는 ldr r0, [r4, #16]. GCC는 아마도 각 GPIO 주소를 별도의 레지스터로로드 할 것입니다. (그중 일부는 Thumb 인코딩에서 회전 된 즉시로 표현 될 수 있지만 리터럴 풀에서
가져온 것

4
내 걱정은 근거가 없다는 것이 밝혀졌습니다. ARM GCC도 이런 방식으로 최적화합니다. godbolt.org/z/ztB7hi . 그러나 당신이 원하는 static volatile uint32_t &MAP0_MODER것은 아니라는 점에 유의하십시오 inline. inline변수는 컴파일되지 않습니다. ( static포인터에 대한 정적 스토리지 volatile를 사용하지 않으며 데드 스토어 제거 또는 쓰기 / 리드 백 최적화를 피하기 위해 MMIO가 원하는 것입니다.)
Peter Cordes

1
@PeterCordes : 인라인 변수는 새로운 C ++ 17 기능입니다. 하지만 당신 말이 맞아요, static이 경우에도 똑같이 잘합니다. 을 언급 해 주셔서 감사 volatile합니다. 제 답변에 추가하겠습니다 (그리고 인라인을 정적으로 변경하므로 C ++ 17 이전 버전에서 작동합니다).
geza

2
이것은 엄격하게 잘 행동이 볼 정의하지 않습니다 이 트위터 스레드를 하고 어쩌면이 일에 유용합니다
Shafik Yaghmour

1
@JFaucher : 가지고있는 구조체만큼 네임 스페이스를 만들고 해당 네임 스페이스에서 독립형 함수를 사용합니다. 따라서 GPIOA::togglePin().
geza

20

임베디드 시스템 영역에서는 구조를 사용하거나 레지스터 주소에 대한 포인터를 정의하여 하드웨어를 모델링 할 수 있습니다.

컴파일러는 정렬 목적으로 멤버 사이에 패딩을 추가 할 수 있으므로 구조 별 모델링은 권장되지 않습니다 (임베디드 시스템의 많은 컴파일러에는 구조 패킹을위한 pragma가 있음).

예:

uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);

배열 표기법을 사용할 수도 있습니다.

uint16_t status = UART1[UART_STATUS_OFFSET];  

IMHO 구조를 사용해야하는 경우 주소를 건너 뛰는 가장 좋은 방법은 멤버를 정의하고 액세스하지 않는 것입니다.

struct UART1
{
  uint16_t status;
  uint16_t reserved1; // Transmit register
  uint16_t receive_register;
};

프로젝트 중 하나에서 우리는 서로 다른 공급 업체의 상수와 구조체를 모두 가지고 있습니다 (공급 업체 1은 상수를 사용하고 공급 업체 2는 구조를 사용함).


답변 해 주셔서 감사합니다. 그러나 자동 완성 기능 (올바른 속성 만 표시됨)을 받았을 때 사용자의 작업을 용이하게하기 위해 구조적 접근 방식을 사용하고 예약 된 슬롯을 다음과 같이 "표시"하고 싶지 않습니다. 내 첫 게시물의 댓글에서 지적했습니다.
J Faucher

static자동 완성이 정적 멤버를 표시 할 수 있다고 가정 하고 위의 주소 멤버를 구조의 멤버 로 만들면 여전히 가능합니다. 그렇지 않은 경우 인라인 멤버 함수일 수도 있습니다.
Phil1970

@JFaucher 나는 임베디드 시스템 사람이 아니며 이것을 테스트하지 않았지만 예약 된 멤버를 비공개로 선언하면 자동 완성 문제가 해결되지 않습니까? (당신은 구조체에 전용 멤버를 선언 할 수 있으며, 사용 public:하고 private:여러 번 당신이 원하는만큼, 필드의 올바른 순서를 얻을 수 있습니다.)
나다니엘

1
@Nathaniel : 그렇지 않습니다. 클래스에 비 정적 데이터 멤버 publicprivate비 정적 데이터 멤버 가 모두 있는 경우 표준 레이아웃 유형 이 아니므로 사용자가 생각하는 순서 보장을 제공하지 않습니다. (그리고 OP의 유스 케이스에는 표준 레이아웃 유형이 필요하다고 확신합니다.)
ruakh

1
volatile메모리 매핑 된 I / O 레지스터에 대한 BTW 선언을 잊지 마십시오 .
Peter Cordes 2018

13

당신이 정말로 이것을 위해 클래스를 사용하고 싶지 않다는 것이 geza의 옳습니다.

그러나 만약 당신이 주장한다면, 사용하지 않는 n 바이트 너비의 멤버를 추가하는 가장 좋은 방법 은 단순히 그렇게하는 것입니다 :

char unused[n];

클래스 멤버에 임의의 패딩이 추가되지 않도록 구현 별 pragma를 추가하면 작동 할 수 있습니다.


GNU C / C ++ (gcc, clang 및 동일한 확장을 지원하는 기타)의 경우 속성을 넣을 수있는 유효한 위치 중 하나는 다음과 같습니다.

#include <stddef.h>
#include <stdint.h>
#include <assert.h>  // for C11 static_assert, so this is valid C as well as C++

struct __attribute__((packed)) GPIO {
    volatile uint32_t a;
    char unused[3];
    volatile uint32_t b;
};

static_assert(offsetof(struct GPIO, b) == 7, "wrong GPIO struct layout");

( = 7 바이트를 보여주는 Godbolt 컴파일러 탐색기의offsetof(GPIO, b))


9

@Clifford 및 @Adam Kotwasinski의 답변을 확장하려면 다음을 수행하십시오.

#define REP10(a)        a a a a a a a a a a
#define REP1034(a)      REP10(REP10(REP10(a))) REP10(a a a) a a a a

struct foo {
        int before;
        REP1034(unsigned int :32;)
        int after;
};
int main(void){
        struct foo bar;
        return 0;
}

의견의 추가 요구 사항 에 따라 내 답변에 귀하의 제안의 변형을 통합했습니다 . 신용이 필요한 곳에 신용.
Clifford

7

Clifford의 답변을 확장하려면 항상 익명의 비트 필드를 매크로 아웃 할 수 있습니다.

그래서 대신

uint32_t :160;

사용하다

#define EMPTY_32_1 \
 uint32_t :32
#define EMPTY_32_2 \
 uint32_t :32;     \ // I guess this also can be replaced with uint64_t :64
 uint32_t :32
#define EMPTY_32_3 \
 uint32_t :32;     \
 uint32_t :32;     \
 uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N

그리고 다음과 같이 사용하십시오.

struct A {
  EMPTY_UINT32(3);
  /* which resolves to EMPTY_32_3, which then resolves to real declarations */
}

불행히도, 당신은 EMPTY_32_X당신이 가지고있는 바이트 만큼 많은 변형 이 필요할 것입니다 :( 그래도 당신의 구조체에 단일 선언을 가질 수 있습니다.


5
Boost CPP 매크로를 사용하면 필요한 모든 매크로를 직접 만들 필요가 없도록 재귀를 사용할 수 있다고 생각합니다.
Peter Cordes 2018

3
계단식으로 배열 할 수 있습니다 (최대 전 처리기 재귀 제한이지만 일반적으로 충분합니다). 그래서 #define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1#define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1
밀알

@PeterCordes 일 수도 있지만 태그는 아마도 부스 C 및 C ++ 호환성이 필요함을 나타냅니다.
Clifford

2
C와 C ++는 동일한 C 전처리기를 사용합니다. C에 필요한 부스트 헤더를 사용할 수 있도록하는 것 외에 다른 문제는 없습니다. CPP 매크로 항목을 별도의 헤더에 넣습니다.
Peter Cordes 2018

1

큰 스페이서를 32 비트 그룹으로 정의합니다.

#define M_32(x)   M_2(M_16(x))
#define M_16(x)   M_2(M_8(x))
#define M_8(x)    M_2(M_4(x))
#define M_4(x)    M_2(M_2(x))
#define M_2(x)    x x

#define SPACER int : 32;

struct {
    M_32(SPACER) M_8(SPACER) M_4(SPACER)
};

1

좀 더 구조를 도입하는 것이 유익하다고 생각합니다. 이는 차례로 스페이서 문제를 해결할 수 있습니다.

변형 이름

플랫 네임 스페이스가 좋지만 문제는 다양한 필드 모음으로 끝나고 모든 관련 필드를 함께 전달하는 간단한 방법이 없다는 것입니다. 또한 익명 공용체에서 익명 구조체를 사용하면 구조체 자체에 대한 참조를 전달하거나 템플릿 매개 변수로 사용할 수 없습니다.

따라서 첫 번째 단계로 다음을 분리하는struct 것이 좋습니다 .

// GpioMap0.h
#pragma once

// #includes

namespace Gpio {
struct Map0 {
    GPIO_MAP0_MODER;
    GPIO_MAP0_OTYPER;
    GPIO_MAP0_OSPEEDR;
    GPIO_MAP0_PUPDR;
    GPIO_MAP0_IDR;
    GPIO_MAP0_ODR;
    GPIO_MAP0_BSRR;
    GPIO_MAP0_LCKR;
    GPIO_MAP0_AFR;
    GPIO_MAP0_BRR;
    GPIO_MAP0_ASCR;
};
} // namespace Gpio

// GpioMap1.h
#pragma once

// #includes

namespace Gpio {
struct Map1 {
    // fields
};
} // namespace Gpio

// ... others headers ...

마지막으로 글로벌 헤더 :

// Gpio.h
#pragma once

#include "GpioMap0.h"
#include "GpioMap1.h"
// ... other headers ...

namespace Gpio {
union Gpio {
    Map0 map0;
    Map1 map1;
    // ... others ...
};
} // namespace Gpio

이제를 작성할 수 void special_map0(Gpio:: Map0 volatile& map);있을뿐만 아니라 사용 가능한 모든 아키텍처에 대한 간략한 개요를 한 눈에 볼 수 있습니다.

간단한 스페이서

정의를 여러 헤더로 분할하면 헤더를 개별적으로 훨씬 더 쉽게 관리 할 수 ​​있습니다.

따라서 요구 사항을 정확히 충족하기위한 초기 접근 방식은 반복되는 std::uint32_t:32;. 예, 기존 8k 라인에 몇 100s 라인을 추가하지만 각 헤더는 개별적으로 더 작기 때문에 나쁘지 않을 수 있습니다.

더 이국적인 솔루션을 고려하고 싶다면 ...

$를 소개합니다.

$C ++ 식별자에 대해 실행 가능한 문자 라는 것은 잘 알려지지 않은 사실입니다 . (숫자와는 달리) 실행 가능한 시작 문자입니다.

$가능성이 눈썹을 제기 할 소스 코드에 나타나는, 그리고 $$$$확실히 코드 리뷰하는 동안 관심을 끌 것입니다. 이것은 당신이 쉽게 이용할 수있는 것입니다 :

#define GPIO_RESERVED(Index_, N_) std::uint32_t $$$$##Index_[N_];

struct Map3 {
    GPIO_RESERVED(0, 6);
    GPIO_MAP2_BSRRL;
    GPIO_MAP2_BSRRH;
    GPIO_RESERVED(1, 5);
};

커밋 전 후크 또는 $$$$커밋 된 C ++ 코드 를 찾고 그러한 커밋을 거부 하는 CI에 간단한 "린트"를 함께 넣을 수도 있습니다 .


1
OP의 특정 사용 사례는 메모리 매핑 된 I / O 레지스터를 컴파일러에 설명하기위한 것임을 기억하십시오. 전체 구조체를 값으로 복사하는 것은 결코 의미가 없습니다 . (그리고 각 멤버 like GPIO_MAP0_MODER는 아마도 volatile.) 이전에 익명의 멤버를 참조하거나 템플릿 매개 변수를 사용하는 것이 유용 할 수 있습니다. 그리고 패딩 구조체의 일반적인 경우는 물론입니다. 그러나 유스 케이스는 OP가 익명으로 남겨둔 이유를 설명합니다.
Peter Cordes 2018

$$$padding##Index_[N_];자동 완성 또는 디버깅시 필드 이름을보다 자명하게 만드는 데 사용할 수 있습니다 . (또는 OP에 따른이 연습의 전체 요점이 메모리 매핑 된 I / O 위치 이름에 대해 자동 완성이 더 좋기 때문에 이름 zz$$$padding을 따라 정렬 GPIO...합니다.)
Peter Cordes

@PeterCordes : 확인하기 위해 답변을 다시 스캔했지만 복사에 대한 언급은 전혀 보지 못했습니다. volatile그래도 수정 된 참조 의 한정자를 잊어 버렸습니다 . 명명에 관해서는; OP까지 맡길 게요. 많은 변형 (패딩, 예약 됨, ...)이 있으며 자동 완성을위한 "최고의"접두사조차도 정렬을 조정하는 아이디어에 감사하지만 현재 IDE에 따라 달라질 수 있습니다.
Matthieu M.

나는 " 구조 할당처럼 들리는 모든 관련 필드를 함께 전달하는 간단한 방법이 아니라 "를 언급하고 있으며, 나머지 문장은 공용체의 구조 구성원 이름 지정에 관한 것입니다.
Peter Cordes

1
@PeterCordes : 나중에 설명 된 것처럼 참조로 전달하려고 생각하고있었습니다. OP의 구조가 특정 아키텍처에만 액세스하는 것으로 정적으로 입증 될 수있는 "모듈"을 생성하는 것을 방해하고 (특정을 참조하여 struct) union결국 아키텍처 특정 비트에서도 모든 곳으로 전파 된다는 점이 어색합니다. 다른 사람들에 대해 덜 신경 쓸 수 있습니다.
Matthieu M.

0

구조체가 MCU I / O 포트 액세스에 사용되어서는 안된다는 데 동의하지만 원래 질문은 다음과 같이 대답 할 수 있습니다.

struct __attribute__((packed)) test {
       char member1;
       char member2;
       volatile struct __attribute__((packed))
       {
       private:
              volatile char spacer_bytes[7];
       }  spacer;
       char member3;
       char member4;
};

컴파일러 구문에 따라 __attribute__((packed))#pragma pack또는 이와 유사한 것으로 바꿔야 할 수 있습니다 .

구조체에서 개인 및 공용 멤버를 혼합하면 일반적으로 메모리 레이아웃이 더 이상 C ++ 표준에 의해 보장되지 않습니다. 그러나 구조체의 모든 비 정적 멤버가 전용 인 경우에도 여전히 POD / 표준 레이아웃으로 간주되며이를 포함하는 구조체도 마찬가지입니다.

어떤 이유로 gcc는 익명 구조체의 멤버가 비공개 인 경우 경고를 생성하므로 이름을 지정해야했습니다. 또는 다른 익명 구조체로 래핑하면 경고가 제거됩니다 (버그 일 수 있음).

참고 spacer데이터가 계속 이런 식으로 액세스 할 수 있도록, 회원이 개인 그 자체입니다 :

(char*)(void*)&testobj.spacer;

그러나 그러한 표현은 명백한 해킹처럼 보이며 실수는 말할 것도없고 정말 좋은 이유 없이는 사용되지 않을 것입니다.


1
사용자는 이름에 이중 밑줄이 포함 된 네임 스페이스에서 식별자를 선언 할 수 없습니다 (C ++에서는 또는 C에서는 처음에만). 그렇게하면 코드의 형식이 잘못됩니다. 이러한 이름은 구현에 예약되어 있으므로 이론적으로는 끔찍하고 미묘하고 변덕스러운 방식으로 귀하의 이름과 충돌 할 수 있습니다. 어쨌든 컴파일러는 코드가 포함되어있는 경우 코드를 유지할 의무가 없습니다. 이러한 이름은 자신의 사용을 위해 '내부'이름을 얻는 빠른 방법이 아닙니다.
underscore_d

고맙습니다.
Jack White

-1

안티 솔루션.

하지 마십시오 : 개인 및 공용 필드를 혼합하십시오.

uniqie 변수 이름을 생성하는 카운터가있는 매크로가 유용할까요?

#define CONCAT_IMPL( x, y ) x##y
#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
#define RESERVED MACRO_CONCAT(Reserved_var, __COUNTER__) 


struct {
    GPIO_MAP1_CRL;
    GPIO_MAP1_CRH;
    GPIO_MAP1_IDR;
    GPIO_MAP1_ODR;
    GPIO_MAP1_BSRR;
    GPIO_MAP1_BRR;
    GPIO_MAP1_LCKR;
private:
    char RESERVED[4];
public:
    GPIO_MAP1_AFRL;
    GPIO_MAP1_AFRH;
private:
    char RESERVED[8];
};


3
확인. 아무도 신경 쓰지 않는다면 나는하지 말아야 할 것에 대한 대답을 남길 것입니다.
Robert Andrzejuk

4
@NicHartley 답변의 수를 고려할 때 우리는 "연구"질문에 가깝습니다. 연구에서 교착 상태에 대한 지식은 여전히 ​​지식이며 다른 사람이 잘못된 길을 택하는 것을 방지합니다. 용기에 +1.
Oliv

1
@Oliv And I -1은 OP에 무언가가 필요했기 때문에이 답변은 요구 사항을 위반했기 때문에 잘못된 답변입니다. 나는 그 사람에 대해 긍정적이든 부정적이든 어떠한 가치 판단도하지 않았다. 나는 우리 둘 다 그것이 나쁘다는 데 동의 할 수 있다고 생각합니다. 그 사람에 대해 말하는 것은이 사이트에서 주제를 벗어납니다. (아이디어를 기여하는 데 시간이 걸릴 기꺼이 비록 IMO의 사람이하고있는 일의 아이디어는 밖으로 이동하지 않는 경우에도, 오른쪽)
기금 모니카의 소송

2
네, 틀 렸습니다. 그러나 나는 어떤 사람들이 같은 생각을하게 될지 두렵다. 댓글과 링크 때문에 방금 배운 것이 나에게 부정적인 점이 아닙니다.
Robert Andrzejuk
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.