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




memory-management low-level (7)

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

समस्या

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

अभी, मैंने इसे एक कुरूप 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 ध्वज नहीं है ...


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

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

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

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


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

लेकिन, यदि आप जोर देते हैं, तो 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) उदाहरण।)


मुझे लगता है कि कुछ और संरचना पेश करना फायदेमंद होगा; बदले में, spacers के मुद्दे को हल कर सकते हैं।

वेरिएंट का नाम बताइए

हालांकि फ्लैट नाम स्थान अच्छे हैं, मुद्दा यह है कि आप खेतों की एक मोटी संग्रह के साथ समाप्त होते हैं और सभी संबंधित क्षेत्रों को एक साथ पारित करने का कोई सरल तरीका नहीं है। इसके अलावा, एक अनाम संघ में अनाम संरचनाओं का उपयोग करके आप स्वयं ही संदर्भों को पास नहीं कर सकते हैं, या उन्हें टेम्पलेट पैरामीटर के रूप में उपयोग कर सकते हैं।

पहले कदम के रूप में, इसलिए, मैं 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); लिख सकता हूं void special_map0(Gpio:: Map0 volatile& map); , साथ ही एक नज़र में सभी उपलब्ध आर्किटेक्चर का त्वरित अवलोकन प्राप्त करें।

सरल Spacers

कई हेडर में परिभाषा विभाजन के साथ, हेडर व्यक्तिगत रूप से बहुत अधिक प्रबंधनीय हैं।

इसलिए, आपकी आवश्यकताओं को पूरा करने के लिए मेरा प्रारंभिक दृष्टिकोण दोहराया std::uint32_t:32; साथ रहना होगा 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 ++ कोड में $$$$ लिए दिखता है और इस तरह के कमेंट्स को अस्वीकार करता है।


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

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

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






bare-metal