C ++ sınıfı bir bellek yapısında nasıl "boşluk bırakıcı" oluşturabilirim?


94

Sorun

Bir de düşük düzeyde çıplak metal gömülü bağlamda, bir erişim, bellek konumu için kullanıcı yasaklamış, C ++ yapı içinde ve herhangi bir isim vermeden, bellek boş bir alan yaratmak istiyoruz.

Şu anda, bunu uint32_t :96;üç sözcüğün yerini alabilecek çirkin bir bitfield koyarak başardım , ancak bu oldukça meşru olan GCC'den (Bitfield uint32_t'ye sığmayacak kadar büyük) bir uyarı getirecek.

İyi çalışsa da, bu uyarıların birkaç yüzünü içeren bir kitaplığı dağıtmak istediğinizde çok temiz değil ...

Bunu nasıl düzgün bir şekilde yaparım?

İlk etapta neden bir sorun var?

Üzerinde çalıştığım proje, tüm bir mikrodenetleyici hattının (STMicroelectronics STM32) farklı çevre birimlerinin bellek yapısını tanımlamaktan ibarettir. Bunu yapmak için, sonuç, hedeflenen mikro denetleyiciye bağlı olarak tüm kayıtları tanımlayan birkaç yapının bir birleşimini içeren bir sınıftır.

Oldukça basit bir çevre birimi için basit bir örnek şudur: Genel Amaçlı Giriş / Çıkış (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;
    };
};

Hepsi GPIO_MAPx_YYYbir makrodur, ya uint32_t :32kayıt tipi olarak tanımlanır (özel bir yapı).

Burada uint32_t :192;hangisinin iyi çalıştığını görüyorsunuz , ancak bir uyarıyı tetikliyor.

Şimdiye kadar düşündüklerim:

Birkaç tane ile değiştirmiş olabilirim uint32_t :32;(burada 6), ancak sahip olduğum bazı aşırı durumlar var uint32_t :1344;(42) (diğerleri arasında). Bu nedenle, yapı oluşturma komut dosyası yazılmış olsa bile, 8k diğerinin üzerine yaklaşık yüz satır eklememeyi tercih ederim.

Tam uyarı mesajı şöyle bir şeydir: width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type(Ne kadar gölgeli olduğunu seviyorum).

Daha doğrusu olurdu değil sadece uyarı kaldırarak bunu çözmek, ancak kullanımı

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

bir çözüm olabilir ... bulursam TheRightFlag. Ancak, bu başlıkta da belirtildiği gibi , gcc/cp/class.cbu üzücü kod bölümü ile:

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

Bu da bize -Wxxxbu uyarıyı kaldıracak bayrak olmadığını söylüyor ...


26
düşündün mü char unused[12];vesaire?
MM

3
Sadece uyarıyı bastırırdım. [class.bit] / 1 davranışını garanti eder uint32_t :192;.
NathanOliver

3
@NathanOliver Ben de seve seve isterdim, ama görünen o ki bu uyarı bastırılamaz (GCC kullanarak) veya bunu nasıl yapacağımı bulamadım. Dahası, hala temiz bir yol değil (ama oldukça tatmin edici olurdu). Doğru "-W" bayrağını bulmayı başardım, ancak bunu yalnızca kendi dosyalarıma uygulamayı başaramadım (Kullanıcının çalışması için bu tür uyarıları kaldırmasını istemiyorum)
J Faucher

3
:42*32Bunun yerine BTW yazabilirsiniz:1344
MM

1
Uyarıları bastırmak için bunu dene? gcc.gnu.org/onlinedocs/gcc/…
Hitobat

Yanıtlar:


36

Birden çok bitişik anonim bit alanı kullanın. Yani bunun yerine:

    uint32_t :160;

örneğin, sahip olacaksınız:

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

Anonim olmasını istediğiniz her kayıt için bir tane.

Doldurmanız gereken geniş alanlarınız varsa, tek 32 bitlik alanı tekrarlamak için makroları kullanmaya daha açık ve daha az hata eğilimi olabilir. Örneğin, verilen:

#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)

Daha sonra 1344 (42 * 32 bit) boşluk şu şekilde eklenebilir:

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

Cevap için teşekkürler. Bunu zaten düşündüm, ancak bazı dosyalarıma 200'den fazla satır ekleyeceğini ( uint32_t :1344;yerinde) bu yüzden bu şekilde gitmek zorunda
kalmamayı

1
@JFaucher Satır sayısı gereksiniminize olası bir çözüm ekledi. Bu tür gereksinimleriniz varsa, bunları karşılamayan yanıtlar almamak için soruda bunlardan bahsedebilirsiniz.
Clifford

Düzenleme için teşekkürler ve satır sayısı konusunu belirtmediğim için üzgünüm. Demek istediğim, kodumun çok fazla satır olduğu ve çok daha fazlasını eklemekten kaçınmayı tercih ettiğimden dolayı zaten acı verici olması. Bu nedenle, birinin bitişik anonim bit alanını kullanmaktan kaçınmanın "temiz" veya "resmi" bir yolunu bilip bilmediğini soruyordum (bu iyi çalışsa bile). Makro yaklaşım bana iyi görünüyor. Bu arada, örneğinizde 36 * 32 bitlik bir alanınız yok mu?
J Faucher

@JFaucher - düzeltildi. G / Ç yazmacı eşleme dosyaları, çok sayıda yazmaç nedeniyle zorunlu olarak büyüktür - normalde bir kez yazarsınız ve donanım sabit olduğu için bakım bir sorun değildir. Kayıtları "gizlemek" dışında, daha sonra bunlara erişmeniz gerekirse, kendiniz için bakım çalışması yaparsınız. Elbette, tüm STM32 cihazlarının satıcı tarafından sağlanan bir kayıt haritası başlığına sahip olduğunun farkındasınız mı? Bunu kullanmak çok daha az hataya meyillidir.
Clifford

2
Size katılıyorum ve adil olmak gerekirse, cevabınızda gösterilen bu iki yöntemden birini kullanacağımı düşünüyorum. Bunu yapmadan önce C ++ 'nın daha iyi bir çözüm sağlamadığından emin olmak istedim. ST'nin bu başlıkları sağladığının farkındayım, ancak bunlar makroların ve bitsel işlemlerin yoğun kullanımı üzerine inşa edilmiştir. Benim projem, bu başlıklara daha az hata eğilimli olacak (enum sınıflarını, bit alanlarını vb. Kullanarak) bir C ++ eşdeğeri oluşturmaktır . Bu nedenle, CMSIS başlıklarını C ++ yapılarımıza "çevirmek" için bir komut dosyası kullanıyoruz (ve btw ST dosyalarında bazı hatalar bulduk)
J Faucher

45

C ++ - ish yöntemine ne dersiniz?

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;
}

Ad GPIOalanı nedeniyle otomatik tamamlama elde edersiniz ve kukla dolguya gerek yoktur. Hatta, her kaydın adresini görebildiğiniz için, derleyicinin doldurma davranışına hiç güvenmek zorunda kalmadığınız için neler olduğu daha açık.


1
Bu, aynı işlevden birden çok MMIO yazmacına erişim için bir yapıdan daha az optimize etmek mümkün olabilir. Bir kayıttaki temel adrese bir işaretçi ile derleyici yükleme / saklama talimatlarını anında yer değiştirmelerle kullanabilir ldr r0, [r4, #16]; derleyicilerin her adres ayrı ayrı bildirildiğinde bu optimizasyonu kaçırma olasılığı daha yüksektir. GCC muhtemelen her bir GPIO adresini ayrı bir kayda yükleyecektir. (Birebir havuzdan, ancak bazıları Başparmak kodlamada döndürülmüş anında gösterimler olarak gösterilebilir.)
Peter Cordes

4
Endişelerimin temelsiz olduğu ortaya çıktı; ARM GCC de bu şekilde optimize eder. godbolt.org/z/ztB7hi . Ama istediğini unutma static volatile uint32_t &MAP0_MODER, değil inline. Bir inlinedeğişken derlemez. ( staticişaretçi için herhangi bir statik depolamaya sahip olmaktan kaçınır ve volatiletam olarak MMIO'nun kayıt dışı kalmayı ortadan kaldırmasını veya yazma / geri okumayı optimize etmesini önlemek için istediğiniz şeydir.)
Peter Cordes

1
@PeterCordes: satır içi değişkenler yeni bir C ++ 17 özelliğidir. Ama haklısın, staticbu dava için eşit derecede iyi. Bahsettiğiniz için teşekkürler, cevabıma volatileekleyeceğim (ve satır içi statik olarak değiştireceğim, böylece C ++ 17 öncesi için çalışıyor).
geza

2
Bu kesinlikle iyi tanımlanmış bir davranış değil bu twitter
başlığına

1
@JFaucher: sahip olduğunuz yapılar kadar çok ad alanı oluşturun ve bu ad alanında bağımsız işlevleri kullanın. Yani sahip olacaksın GPIOA::togglePin().
geza

20

Gömülü sistemler alanında, bir yapı kullanarak veya kayıt adreslerine işaretçiler tanımlayarak donanımı modelleyebilirsiniz.

Yapıya göre modelleme, derleyicinin hizalama amacıyla üyeler arasında dolgu eklemesine izin verildiğinden (gömülü sistemler için birçok derleyicinin yapıyı paketlemek için bir pragması olmasına rağmen) önerilmez.

Misal:

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);

Ayrıca dizi gösterimini de kullanabilirsiniz:

uint16_t status = UART1[UART_STATUS_OFFSET];  

IMHO yapısını kullanmanız gerekiyorsa, adresleri atlamanın en iyi yöntemi bir üye tanımlamak ve ona erişmek değil:

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

Projelerimizden birinde, farklı satıcılardan hem sabitler hem de yapılar var (satıcı 1 sabitleri kullanırken satıcı 2 yapıları kullanıyor).


Cevabınız için teşekkürler. Ancak, otomatik tamamlama özelliği aldığında kullanıcının işini kolaylaştırmak için bir yapı yaklaşımı kullanmayı seçiyorum (Yalnızca doğru özniteliklere sahip olacaksınız) ve kullanıcıya ayrılmış yuvaları şu şekilde "göstermek" istemiyorum: ilk yazımın bir yorumunda dikkat çekti.
J Faucher

staticOtomatik tamamlamanın statik üyeler gösterebildiğini varsayarak yukarıdaki adres üyelerini bir yapının yukarıdaki adres üyelerini yaparak yine de buna sahip olabilirsiniz . Değilse, aynı zamanda satır içi üye işlevleri de olabilir.
Phil1970

@JFaucher Ben bir gömülü sistemler çalışanı değilim ve bunu test etmedim, ancak otomatik tamamlama sorunu rezerve edilmiş üyeyi özel ilan ederek çözülmez mi? (Bir yapıda özel üyeler ilan edebilirsiniz public:ve private:alanların doğru sıralanmasını elde etmek için istediğiniz kadar kullanabilirsiniz.)
Nathaniel

1
@Nathaniel: Öyle değil; Bir sınıf hem sahipse publicve privatestatik olmayan veri üyeleri, o zaman değil standart düzen tipi bunun sen düşünme sipariş teminatı sağlamaz yüzden. (Ve OP'nin kullanım durumunun standart bir düzen türü gerektirdiğinden oldukça eminim.)
ruakh

1
volatileBellek eşlemeli G / Ç kayıtları için bu bildirimleri, BTW'yi unutmayın .
Peter Cordes

13

bunun için sınıfları kullanmak istemediğin konusunda haklısın.

Ancak, ısrar edecekseniz, n bayt genişliğinde kullanılmayan bir üye eklemenin en iyi yolu basitçe bunu yapmaktır:

char unused[n];

Sınıf üyelerine rastgele doldurmanın eklenmesini önlemek için uygulamaya özgü bir pragma eklerseniz, bu işe yarayabilir.


GNU C / C ++ (gcc, clang ve aynı uzantıları destekleyen diğerleri) için, niteliği koymak için geçerli yerlerden biri şudur:

#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");

( Godbolt derleyici kaşifindeoffsetof(GPIO, b) = 7 bayt gösteren örnek .)


9

@ Clifford ve @Adam Kotwasinski'nin yanıtlarını genişletmek için:

#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;
}

Bir yorumdaki diğer gereksinimleri takiben cevabıma önerinizin bir çeşidini ekledim. Kredinin ödenmesi gereken kredi.
Clifford

7

Clifford'un cevabını genişletmek için, anonim bit alanlarını her zaman makro yapabilirsiniz.

Yani yerine

uint32_t :160;

kullanım

#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

Ve sonra onu şu şekilde kullan

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

Ne yazık ki, sahip olduğunuz EMPTY_32_Xbayta kadar değişkene ihtiyacınız olacak :( Yine de, yapınızda tek bildirimlere sahip olmanıza izin verir.


5
Boost CPP makrolarını kullanarak, gerekli tüm makroları elle oluşturmak zorunda kalmamak için özyinelemeyi kullanabileceğinizi düşünüyorum.
Peter Cordes

3
Bunları basamaklandırabilirsiniz (önişlemci yineleme sınırına kadar, ancak bu genellikle fazladır). Yani #define EMPTY_32_2 EMPTY_32_1; EMPTY_32_1ve #define EMPTY_32_3 EMPTY_32_2; EMPTY_32_1vb
Miral

@PeterCordes belki, ama etiketler belki de kabin C ve C ++ uyumluluğunun gerekli olduğunu gösteriyor.
Clifford

2
C ve C ++ aynı C ön işlemcisini kullanır; C için gerekli yükseltme başlığını sağlamaktan başka bir sorun görmüyorum. CPP-makro şeylerini ayrı bir başlığa koyuyorlar.
Peter Cordes

1

32 bitlik gruplar olarak büyük bir ara parça tanımlamak için.

#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

Biraz daha yapı getirmenin faydalı olacağını düşünüyorum; bu da ara parça sorununu çözebilir.

Varyantları adlandırın

Düz ad alanları güzel olsa da, sorun, rengarenk bir alan koleksiyonuna sahip olmanız ve ilgili tüm alanları bir araya getirmenin basit bir yolunu bulmamanızdır. Ayrıca, anonim bir birleşimde anonim yapılar kullanarak, yapıların kendilerine referanslar iletemez veya bunları şablon parametreleri olarak kullanamazsınız.

İlk adım olarak, bu nedenle, şu hususları kırmayıstruct düşünürdüm :

// 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 ...

Ve son olarak, genel başlık:

// Gpio.h
#pragma once

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

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

Şimdi, void special_map0(Gpio:: Map0 volatile& map);bir bakışta mevcut tüm mimarilere hızlı bir bakış atmanın yanı sıra bir yazabilirim .

Basit Paspayı

Tanım birden çok başlığa bölündüğünde, başlıklar ayrı ayrı çok daha yönetilebilir.

Bu nedenle, gereksinimlerinizi tam olarak karşılamak için ilk yaklaşımım, tekrarlananlara bağlı kalmak olacaktır std::uint32_t:32;. Evet, mevcut 8k satırlarına birkaç 100s satır ekler, ancak her başlık ayrı ayrı daha küçük olduğu için o kadar da kötü olmayabilir.

Yine de daha egzotik çözümler düşünmeye istekliysen ...

$ İle tanışın.

$C ++ tanımlayıcıları için geçerli bir karakter olduğu az bilinen bir gerçektir ; hatta geçerli bir başlangıç ​​karakteridir (rakamların aksine).

$Kaynak kodda bir görünme muhtemelen kaşları kaldıracaktır ve $$$$kod incelemeleri sırasında kesinlikle dikkat çekecektir. Bu, kolayca yararlanabileceğiniz bir şeydir:

#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);
};

Hatta bir ön-kesinleştirme kancası olarak veya CI'nizde $$$$, işlenmiş C ++ kodunu arayan ve bu tür taahhütleri reddeden basit bir "tüy" bile oluşturabilirsiniz .


1
OP'nin özel kullanım durumunun, derleyiciye bellek eşlemeli G / Ç kayıtlarını açıklamak için olduğunu unutmayın. Bu asla değerine göre bütün yapı kopyalamak anlamda yapar. (Ve her üyenin benzer GPIO_MAP0_MODERolması muhtemeldir volatile.) Yine de, önceden anonim üyelerin referans veya şablon-parametre kullanımı faydalı olabilir. Ve dolgu yapılarının genel durumu için, elbette. Ancak kullanım durumu, OP'nin onları neden isimsiz bıraktığını açıklıyor.
Peter Cordes

$$$padding##Index_[N_];Otomatik tamamlamada veya hata ayıklama sırasında ortaya çıkarsa, alan adını daha açıklayıcı hale getirmek için kullanabilirsiniz . (Ya da isimlerden zz$$$paddingsonra sıralamak için GPIO..., çünkü OP'ye göre bu alıştırmanın tüm amacı, bellek eşlemeli G / Ç konum adları için daha güzel otomatik tamamlamadır.)
Peter Cordes

@PeterCordes: Kontrol etmek için cevabı tekrar taradım ve kopyalamadan hiç bahsetmedim. volatileYine de referans üzerindeki niteleyiciyi unuttum , ki bu düzeltildi. İsimlendirmeye gelince; OP'ye kadar çıkmasına izin vereceğim. Pek çok varyasyon var (doldurma, ayrılmış, ...) ve otomatik tamamlama için "en iyi" önek bile eldeki IDE'ye bağlı olabilir, ancak sıralamayı değiştirme fikrini takdir ediyorum.
Matthieu M.

Yapı ataması gibi gelen " ve tüm ilgili alanları bir araya getirmenin basit bir yolu değil " den ve sendikanın yapı üyelerini adlandırmakla ilgili cümlenin geri kalanından bahsediyordum.
Peter Cordes

1
@PeterCordes: Daha sonra açıklanacağı gibi, referansla geçmeyi düşünüyordum. OP'nin yapısının, yalnızca belirli bir mimariye eriştiği statik olarak kanıtlanabilen (belirli bir mimariye atıfta bulunarak struct) "modüller" oluşturmasını engellemesini unionve mimariye özgü bitlerde bile sonuçların her yere yayılmasını garip buluyorum. diğerleri hakkında daha az umursayabilirdi.
Matthieu M.

0

Yapıların MCU G / Ç bağlantı noktası erişimi için kullanılmaması gerektiğini kabul etmeme rağmen, orijinal soru şu şekilde yanıtlanabilir:

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

Sen değiştirmeniz gerekebilir __attribute__((packed))ile #pragma packveya benzeri derleyici sözdizimi bağlı.

Bir yapıda özel ve genel üyelerin karıştırılması normalde bellek düzeninin artık C ++ standardı tarafından garanti edilmemesiyle sonuçlanır. Bununla birlikte , bir yapının statik olmayan tüm üyeleri özelse, yine de POD / standart düzen ve onları gömen yapılar olarak kabul edilir.

Bazı nedenlerden ötürü, anonim yapının bir üyesi özelse gcc bir uyarı üretiyor, bu yüzden ona bir isim vermem gerekiyordu. Alternatif olarak, onu başka bir anonim yapıya sarmak da uyarıdan kurtulur (bu bir hata olabilir).

spacerÜyenin kendisinin özel olmadığını unutmayın , bu nedenle verilere yine de bu yolla erişilebilir:

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

Bununla birlikte, böyle bir ifade apaçık bir hack gibi görünür ve umarım, bırakın hata olarak, gerçekten iyi bir sebep olmadan kullanılamaz.


1
Kullanıcılar, adın herhangi bir yerinde çift alt çizgi içeren herhangi bir ad alanında tanımlayıcılar bildiremezler (C ++ 'da veya yalnızca C' nin başında); böyle yapmak kodun biçimini bozar. Bu isimler uygulama için ayrılmıştır ve bu nedenle teoride sizinkiyle korkunç derecede ince ve kaprisli şekillerde çelişebilir. Her neyse, derleyicinin kodu içeriyorsa kodunuzu saklama yükümlülüğü yoktur. Bu tür isimler, kendi kullanımınız için 'dahili' isimler almanın hızlı bir yolu değildir.
underscore_d

Teşekkürler, düzelttim.
Jack White

-1

Anti-çözüm.

BUNU YAPMAYIN: Özel ve genel alanları karıştırın.

Belki uniqie değişken adları oluşturmak için bir sayacı olan bir makro yararlı olabilir?

#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
Tamam. Kimsenin sakıncası yoksa, cevabı ne yapmamam olarak bırakacağım.
Robert Andrzejuk

4
@NicHartley Cevap sayısına bakıldığında bir "araştırma" sorusuna yaklaşıyoruz. Araştırmada, çıkmazların bilgisi hala bilgidir, başkalarının yanlış yola sapmasını önler. Cesaret için +1.
Oliv

1
@Oliv And I -1'd çünkü OP bir şey gerektirdi, bu cevap gereksinimi ihlal etti ve bu nedenle kötü bir cevap. Her iki yorumda da kişi hakkında olumlu ya da olumsuz herhangi bir değer yargısında bulunmadım - sadece cevap üzerine. Sanırım ikimiz de bunun kötü olduğuna hemfikiriz. Kişi hakkında söylenenler bu site için konu dışıdır. (IMO, bir fikre katkıda bulunmak için biraz zaman ayırmaya istekli olsa da, fikir
Fund Monica's Lawsuit

2
Evet yanlış cevap. Ama bazı insanların aynı fikre gelmesinden korkuyorum. Yorum ve bağlantı sayesinde yeni öğrendiğim bir şey, bu benim için olumsuz noktalar değil.
Robert Andrzejuk
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.