मैं C++ क्लास मेमोरी स्ट्रक्चर में "स्पेसर" कैसे बना सकता हूं?




memory-management low-level (8)

विरोधी समाधान।

यह मत करो: निजी और सार्वजनिक क्षेत्रों को मिलाएं।

शायद 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];
};

समस्या

निम्न स्तर के नंगे-धातु एम्बेडेड संदर्भ में, मैं मेमोरी में एक रिक्त स्थान बनाना चाहूंगा, सी ++ संरचना के भीतर और किसी भी नाम के बिना, उपयोगकर्ता को ऐसे मेमोरी स्थान तक पहुंचने के लिए मना कर सकता है।

अभी, मैंने इसे एक कुरूप uint32_t :96; डालकर हासिल किया है uint32_t :96; बिटफील्ड जो आसानी से तीन शब्दों की जगह ले लेगा, लेकिन यह जीसीसी (बिटफील्ड बहुत बड़े 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 :192; जो अच्छी तरह से काम करता है, लेकिन यह एक चेतावनी को ट्रिगर करता है।

मैंने अब तक क्या विचार किया है:

मैंने इसे कई uint32_t :32; बदल दिया होगा uint32_t :32; (6 यहां), लेकिन मेरे पास कुछ चरम मामले हैं जहां मेरे पास uint32_t :1344; (४२) (दूसरों के बीच)। इसलिए मैं इसके बजाय 8k दूसरों के शीर्ष पर लगभग एक सौ पंक्तियों को नहीं जोड़ूंगा, भले ही संरचना पीढ़ी स्क्रिप्टेड हो।

सटीक चेतावनी संदेश कुछ इस तरह है: 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 मिल TheRightFlag । हालाँकि, जैसा कि इस धागे में बताया गया है, इस उदास कोड भाग के साथ gcc/cp/class.c :

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

जो हमें बताता है कि इस चेतावनी को हटाने के लिए कोई -Wxxx ध्वज नहीं है ...


यद्यपि मैं मानता हूं कि एमसीयू 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 या समान के साथ बदलने की आवश्यकता हो सकती है।

एक संरचना में सामान्य रूप से निजी और सार्वजनिक सदस्यों को मिलाकर उस मेमोरी लेआउट का परिणाम सी + + मानक द्वारा गारंटीकृत नहीं होता है। हालाँकि यदि किसी संरचना के सभी गैर-स्थैतिक सदस्य निजी हैं, तो इसे अभी भी POD / मानक लेआउट माना जाता है, और इसलिए ऐसी संरचनाएँ हैं जो उन्हें एम्बेड करती हैं।

किसी कारण से gcc एक चेतावनी उत्पन्न करता है यदि कोई अनाम संरचना का सदस्य निजी है तो मुझे इसे एक नाम देना होगा। वैकल्पिक रूप से, इसे एक अन्य अनाम संरचना में लपेटने से भी चेतावनी से छुटकारा मिल जाता है (यह एक बग हो सकता है)।

ध्यान दें कि spacer सदस्य स्वयं निजी नहीं है, इसलिए डेटा अभी भी इस तरह एक्सेस किया जा सकता है:

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

हालांकि इस तरह की अभिव्यक्ति एक स्पष्ट हैक की तरह दिखती है, और उम्मीद है कि इसका उपयोग वास्तव में अच्छे कारण के बिना नहीं किया जाएगा, एक गलती के रूप में अकेले चलो।


क्लिफोर्ड के उत्तर पर विस्तार करने के लिए, आप हमेशा अनाम बिटफ़िल्ड को मैक्रो कर सकते हैं।

इसलिए इसके बजाय

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 वेरिएंट की आवश्यकता होगी :( फिर भी, यह आपको अपनी संरचना में एकल घोषणाएं करने की अनुमति देता है।


@ क्लिफर्ड और @Aad 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;
}

भूजा का अधिकार जिसे आप वास्तव में इसके लिए उपयोग नहीं करना चाहते हैं।

लेकिन, यदि आप जोर देते हैं, तो n बाइट्स की चौड़ाई के अप्रयुक्त सदस्य को जोड़ने का सबसे अच्छा तरीका, बस इतना करना है:

char unused[n];

यदि आप कक्षा के सदस्यों की मनमानी पैडिंग को रोकने के लिए एक कार्यान्वयन-विशिष्ट प्रस्तावना जोड़ते हैं, तो यह काम कर सकता है।

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

( गॉडबोल्ट कंपाइलर एक्सप्लोरर पर offsetof(GPIO, b) = 7 बाइट्स दिखाते offsetof(GPIO, b) उदाहरण।)


कैसे एक सी ++ के बारे में - ish रास्ता?

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 नाम स्थान के कारण आपको स्वतः पूर्णता प्राप्त होती है, और डमी पैडिंग की कोई आवश्यकता नहीं होती है। यहां तक ​​कि यह अधिक स्पष्ट है कि क्या चल रहा है, जैसा कि आप प्रत्येक रजिस्टर का पता देख सकते हैं, आपको कंपाइलर के गद्दी व्यवहार पर बिल्कुल भी भरोसा करने की आवश्यकता नहीं है।


कई आसन्न अनाम बिटफ़िल्ड का उपयोग करें। इसलिए इसके बजाय:

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

एम्बेडेड सिस्टम क्षेत्र में, आप किसी संरचना का उपयोग करके या रजिस्टर पते पर पॉइंटर्स को परिभाषित करके हार्डवेयर मॉडल कर सकते हैं।

संरचना द्वारा मॉडलिंग की सिफारिश नहीं की जाती है क्योंकि संकलक प्रयोजनों के लिए संकलक को सदस्यों के बीच पैडिंग जोड़ने की अनुमति दी जाती है (हालांकि एम्बेडेड सिस्टम के लिए कई संकलक संरचना को पैक करने के लिए एक व्यावहारिक है)।

उदाहरण:

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 संरचनाओं का उपयोग करता है)।





bare-metal