यह अनुभाग HIDL डेटा प्रकारों का वर्णन करता है। कार्यान्वयन विवरण के लिए, HIDL C++ (C++ कार्यान्वयन के लिए) या HIDL Java (जावा कार्यान्वयन के लिए) देखें।
C++ की समानता में शामिल हैं:
-
structs
C++ सिंटैक्स का उपयोग करती हैं;unions
डिफ़ॉल्ट रूप से C++ सिंटैक्स का समर्थन करती हैं। दोनों का नाम अवश्य होना चाहिए; अनाम संरचनाएं और यूनियन समर्थित नहीं हैं। - HIDL में टाइपडिफ की अनुमति है (क्योंकि वे C++ में हैं)।
- C++-शैली टिप्पणियों की अनुमति है और इन्हें जेनरेट की गई हेडर फ़ाइल में कॉपी किया जाता है।
जावा की समानताओं में शामिल हैं:
- प्रत्येक फ़ाइल के लिए, HIDL एक जावा-शैली नेमस्पेस को परिभाषित करता है जो
android.hardware.
. उत्पन्न C++ नेमस्पेस::android::hardware::…
है। - फ़ाइल की सभी परिभाषाएँ जावा-शैली
interface
रैपर में समाहित हैं। - HIDL सरणी घोषणाएँ जावा शैली का अनुसरण करती हैं, C++ शैली का नहीं। उदाहरण:
struct Point { int32_t x; int32_t y; }; Point[3] triangle; // sized array
- टिप्पणियाँ javadoc प्रारूप के समान हैं।
डेटा प्रतिनिधित्व
स्टैंडर्ड-लेआउट (सादे-पुराने-डेटा प्रकारों की आवश्यकता का एक उपसमूह) से बनी एक struct
या union
में उत्पन्न C++ कोड में एक सुसंगत मेमोरी लेआउट होता है, जो struct
और union
सदस्यों पर स्पष्ट संरेखण विशेषताओं के साथ लागू होता है।
आदिम HIDL प्रकार, साथ ही enum
और bitfield
प्रकार (जो हमेशा आदिम प्रकार से प्राप्त होते हैं), cstdint से मानक C++ प्रकार जैसे std::uint32_t
पर मैप करते हैं।
चूँकि जावा अहस्ताक्षरित प्रकारों का समर्थन नहीं करता है, अहस्ताक्षरित HIDL प्रकारों को संबंधित हस्ताक्षरित जावा प्रकार में मैप किया जाता है। जावा कक्षाओं के लिए संरचना मानचित्र; जावा सरणियों के लिए सरणियाँ मानचित्र; यूनियन वर्तमान में जावा में समर्थित नहीं हैं। स्ट्रिंग्स को आंतरिक रूप से UTF8 के रूप में संग्रहीत किया जाता है। चूँकि जावा केवल UTF16 स्ट्रिंग्स का समर्थन करता है, इसलिए जावा कार्यान्वयन से या उससे भेजे गए स्ट्रिंग मानों का अनुवाद किया जाता है, और पुन: अनुवाद पर समान नहीं हो सकते हैं क्योंकि वर्ण सेट हमेशा सुचारू रूप से मैप नहीं होते हैं।
C++ में IPC पर प्राप्त डेटा को const
रूप में चिह्नित किया जाता है और यह केवल-पढ़ने योग्य मेमोरी में होता है जो केवल फ़ंक्शन कॉल की अवधि के लिए बना रहता है। जावा में आईपीसी पर प्राप्त डेटा को पहले ही जावा ऑब्जेक्ट्स में कॉपी किया जा चुका है, इसलिए इसे अतिरिक्त प्रतिलिपि के बिना बनाए रखा जा सकता है (और संशोधित किया जा सकता है)।
एनोटेशन
टाइप घोषणाओं में जावा-शैली एनोटेशन जोड़े जा सकते हैं। एनोटेशन को एचआईडीएल कंपाइलर के वेंडर टेस्ट सूट (वीटीएस) बैकएंड द्वारा पार्स किया जाता है लेकिन ऐसे पार्स किए गए एनोटेशन में से कोई भी वास्तव में एचआईडीएल कंपाइलर द्वारा समझा नहीं जाता है। इसके बजाय, पार्स किए गए वीटीएस एनोटेशन को वीटीएस कंपाइलर (वीटीएससी) द्वारा नियंत्रित किया जाता है।
एनोटेशन जावा सिंटैक्स का उपयोग करते हैं: @annotation
या @annotation(value)
या @annotation(id=value, id=value…)
जहां मान या तो एक स्थिर अभिव्यक्ति, एक स्ट्रिंग, या {}
के अंदर मानों की एक सूची हो सकता है, जैसे कि जावा। एक ही नाम की अनेक टिप्पणियाँ एक ही आइटम से संलग्न की जा सकती हैं।
अग्रेषित घोषणाएँ
एचआईडीएल में, संरचनाओं को अग्रेषित-घोषित नहीं किया जा सकता है, जिससे उपयोगकर्ता-परिभाषित, स्व-संदर्भित डेटा प्रकार असंभव हो जाते हैं (उदाहरण के लिए, आप एचआईडीएल में एक लिंक की गई सूची या पेड़ का वर्णन नहीं कर सकते हैं)। अधिकांश मौजूदा (एंड्रॉइड 8.x से पहले) एचएएल में फ़ॉर्वर्ड घोषणाओं का सीमित उपयोग होता है, जिसे डेटा संरचना घोषणाओं को पुनर्व्यवस्थित करके हटाया जा सकता है।
यह प्रतिबंध स्व-संदर्भित डेटा संरचना में कई बार होने वाले पॉइंटर मानों पर नज़र रखने के बजाय, डेटा संरचनाओं को एक साधारण डीप-कॉपी के साथ मूल्य के आधार पर कॉपी करने की अनुमति देता है। यदि एक ही डेटा दो बार पारित किया जाता है, जैसे कि दो विधि पैरामीटर या vec<T>
s जो एक ही डेटा को इंगित करते हैं, तो दो अलग-अलग प्रतियां बनाई और वितरित की जाती हैं।
नेस्टेड घोषणाएँ
एचआईडीएल वांछित कई स्तरों पर नेस्टेड घोषणाओं का समर्थन करता है (नीचे उल्लेखित एक अपवाद को छोड़कर)। उदाहरण के लिए:
interface IFoo { uint32_t[3][4][5][6] multidimArray; vec<vec<vec<int8_t>>> multidimVector; vec<bool[4]> arrayVec; struct foo { struct bar { uint32_t val; }; bar b; } struct baz { foo f; foo.bar fb; // HIDL uses dots to access nested type names } …
अपवाद यह है कि इंटरफ़ेस प्रकार केवल vec<T>
में एम्बेड किए जा सकते हैं और केवल एक स्तर गहरा (कोई vec<vec<IFoo>>
नहीं)।
कच्चा सूचक वाक्यविन्यास
HIDL भाषा * का उपयोग नहीं करती है और C/C++ रॉ पॉइंटर्स के पूर्ण लचीलेपन का समर्थन नहीं करती है। एचआईडीएल पॉइंटर्स और एरे/वेक्टर को कैसे इनकैप्सुलेट करता है, इसके विवरण के लिए, vec<T> टेम्पलेट देखें।
इंटरफेस
interface
कीवर्ड के दो उपयोग हैं।
- यह एक .hal फ़ाइल में इंटरफ़ेस की परिभाषा को खोलता है।
- इसका उपयोग संरचना/संघ क्षेत्रों, विधि मापदंडों और रिटर्न में एक विशेष प्रकार के रूप में किया जा सकता है। इसे एक सामान्य इंटरफ़ेस और
android.hidl.base@1.0::IBase
के पर्याय के रूप में देखा जाता है।
उदाहरण के लिए, IServiceManager
के पास निम्नलिखित विधि है:
get(string fqName, string name) generates (interface service);
विधि नाम से कुछ इंटरफ़ेस देखने का वादा करती है। इंटरफ़ेस को android.hidl.base@1.0::IBase
से बदलना भी समान है।
इंटरफ़ेस को केवल दो तरीकों से पारित किया जा सकता है: शीर्ष-स्तरीय पैरामीटर के रूप में, या vec<IMyInterface>
के सदस्यों के रूप में। वे नेस्टेड वेक्स, स्ट्रक्चर्स, एरेज़ या यूनियनों के सदस्य नहीं हो सकते।
MQDescriptorSync और MQDescriptorUnsync
MQDescriptorSync
और MQDescriptorUnsync
प्रकार एक HIDL इंटरफ़ेस में एक सिंक्रनाइज़ या अनसिंक्रनाइज़्ड फास्ट मैसेज क्यू (FMQ) डिस्क्रिप्टर पास करते हैं। विवरण के लिए, HIDL C++ देखें (FMQ जावा में समर्थित नहीं हैं)।
मेमोरी प्रकार
memory
प्रकार का उपयोग HIDL में अनमैप्ड साझा मेमोरी को दर्शाने के लिए किया जाता है। यह केवल C++ में समर्थित है। इस प्रकार के मान का उपयोग IMemory
ऑब्जेक्ट को प्रारंभ करने, मेमोरी को मैप करने और इसे उपयोग करने योग्य बनाने के लिए प्राप्तकर्ता छोर पर किया जा सकता है। विवरण के लिए, HIDL C++ देखें।
चेतावनी: साझा मेमोरी में रखा गया संरचित डेटा एक प्रकार का होना चाहिए जिसका प्रारूप memory
से गुजरने वाले इंटरफ़ेस संस्करण के जीवनकाल तक कभी नहीं बदलेगा। अन्यथा, एचएएल को घातक संगतता समस्याओं का सामना करना पड़ सकता है।
सूचक प्रकार
pointer
प्रकार केवल HIDL आंतरिक उपयोग के लिए है।
बिटफ़ील्ड<टी> प्रकार टेम्पलेट
bitfield<T>
जिसमें T
एक उपयोगकर्ता-परिभाषित एनम है, सुझाव देता है कि मान T
में परिभाषित एनम मानों का एक बिटवाइज़-OR है। जेनरेट किए गए कोड में, bitfield<T>
टी के अंतर्निहित प्रकार के रूप में प्रकट होता है। उदाहरण के लिए:
enum Flag : uint8_t { HAS_FOO = 1 << 0, HAS_BAR = 1 << 1, HAS_BAZ = 1 << 2 }; typedef bitfield<Flag> Flags; setFlags(Flags flags) generates (bool success);
कंपाइलर फ़्लैग्स प्रकार को uint8_t
के समान ही संभालता है।
(u)int8_t
/ (u)int16_t
/ (u)int32_t
/ (u)int64_t
का उपयोग क्यों न करें? bitfield
का उपयोग पाठक को अतिरिक्त एचएएल जानकारी प्रदान करता है, जो अब जानता है कि setFlags
का बिटवाइज-ओआर मान लेता है (यानी जानता है कि 16 के साथ setFlags
कॉल करना अमान्य है)। bitfield
के बिना, यह जानकारी केवल दस्तावेज़ीकरण के माध्यम से बताई जाती है। इसके अलावा, वीटीएस वास्तव में जांच कर सकता है कि झंडे का मूल्य बिटवाइज़-या ध्वज का है या नहीं।
आदिम प्रकार को संभालें
चेतावनी: किसी भी प्रकार के पते (भौतिक डिवाइस पते भी) कभी भी मूल हैंडल का हिस्सा नहीं होने चाहिए। प्रक्रियाओं के बीच इस जानकारी को प्रसारित करना खतरनाक है और उन पर हमला होने की आशंका है। किसी प्रक्रिया के भीतर आवंटित मेमोरी को देखने के लिए उपयोग किए जाने से पहले प्रक्रियाओं के बीच पारित किसी भी मान को मान्य किया जाना चाहिए। अन्यथा, ख़राब हैंडल ख़राब मेमोरी एक्सेस या मेमोरी भ्रष्टाचार का कारण बन सकते हैं।
HIDL शब्दार्थ कॉपी-दर-वैल्यू हैं, जिसका अर्थ है कि पैरामीटर कॉपी किए गए हैं। डेटा का कोई भी बड़ा टुकड़ा, या डेटा जिसे प्रक्रियाओं के बीच साझा करने की आवश्यकता होती है (जैसे कि एक सिंक बाड़), लगातार वस्तुओं की ओर इशारा करने वाले फ़ाइल डिस्क्रिप्टर के चारों ओर से गुजरते हुए नियंत्रित किया जाता है: साझा मेमोरी, वास्तविक फ़ाइलों, या किसी अन्य चीज़ के लिए ashmem
जो पीछे छिप सकती है एक फ़ाइल डिस्क्रिप्टर. बाइंडर ड्राइवर फ़ाइल डिस्क्रिप्टर को अन्य प्रक्रिया में डुप्लिकेट करता है।
देशी_हैंडल_टी
एंड्रॉइड native_handle_t
का समर्थन करता है, जो libcutils
में परिभाषित एक सामान्य हैंडल अवधारणा है।
typedef struct native_handle { int version; /* sizeof(native_handle_t) */ int numFds; /* number of file-descriptors at &data[0] */ int numInts; /* number of ints at &data[numFds] */ int data[0]; /* numFds + numInts ints */ } native_handle_t;
एक देशी हैंडल इनट्स और फ़ाइल डिस्क्रिप्टर का एक संग्रह है जो मूल्य के अनुसार पारित हो जाता है। एक एकल फ़ाइल डिस्क्रिप्टर को बिना किसी इन्ट और एक फ़ाइल डिस्क्रिप्टर के मूल हैंडल में संग्रहीत किया जा सकता है। handle
आदिम प्रकार के साथ संलग्न देशी हैंडल का उपयोग करके पासिंग हैंडल यह सुनिश्चित करता है कि देशी हैंडल सीधे एचआईडीएल में शामिल हैं।
चूंकि native_handle_t
आकार परिवर्तनशील है, इसलिए इसे सीधे किसी संरचना में शामिल नहीं किया जा सकता है। एक हैंडल फ़ील्ड एक अलग से आवंटित native_handle_t
के लिए एक पॉइंटर उत्पन्न करता है।
एंड्रॉइड के पुराने संस्करणों में, libcutils में मौजूद समान फ़ंक्शन का उपयोग करके मूल हैंडल बनाए गए थे। Android 8.0 और उच्चतर में, ये फ़ंक्शन अब android::hardware::hidl
नेमस्पेस में कॉपी किए गए हैं या NDK में ले जाए गए हैं। HIDL ऑटोजेनरेटेड कोड उपयोगकर्ता-लिखित कोड की भागीदारी के बिना, इन कार्यों को स्वचालित रूप से क्रमबद्ध और डिसेरिएलाइज़ करता है।
डिस्क्रिप्टर स्वामित्व को संभालें और फ़ाइल करें
जब आप एक HIDL इंटरफ़ेस विधि को कॉल करते हैं जो hidl_handle
ऑब्जेक्ट (या तो शीर्ष-स्तर या किसी कंपाउंड प्रकार का हिस्सा) को पास करता है (या लौटाता है), तो इसमें मौजूद फ़ाइल डिस्क्रिप्टर का स्वामित्व इस प्रकार है:
- एक तर्क के रूप में
hidl_handle
ऑब्जेक्ट को पास करने वाला कॉलरnative_handle_t
में निहित फ़ाइल डिस्क्रिप्टर के स्वामित्व को बरकरार रखता है जिसे वह लपेटता है; कॉल करने वाले को इन फ़ाइल डिस्क्रिप्टरों का काम पूरा हो जाने पर उन्हें बंद करना होगा। -
hidl_handle
ऑब्जेक्ट को वापस करने की प्रक्रिया (इसे_cb
फ़ंक्शन में पास करके) ऑब्जेक्ट द्वारा लपेटे गएnative_handle_t
में निहित फ़ाइल डिस्क्रिप्टर का स्वामित्व बरकरार रखती है; जब प्रक्रिया इन फ़ाइल डिस्क्रिप्टरों के साथ पूरी हो जाए तो उन्हें बंद करना होगा। - एक ट्रांसपोर्ट जो
hidl_handle
प्राप्त करता है, उसके पास ऑब्जेक्ट द्वारा लपेटे गएnative_handle_t
के अंदर फ़ाइल डिस्क्रिप्टर का स्वामित्व होता है; रिसीवर लेनदेन कॉलबैक के दौरान इन फ़ाइल डिस्क्रिप्टरों का उपयोग कर सकता है, लेकिन कॉलबैक से परे फ़ाइल डिस्क्रिप्टरों का उपयोग करने के लिए मूल हैंडल को क्लोन करना होगा। लेन-देन पूरा होने पर ट्रांसपोर्ट स्वचालित रूप से फ़ाइल डिस्क्रिप्टर कोclose()
देगा।
एचआईडीएल जावा में हैंडल का समर्थन नहीं करता है (क्योंकि जावा बिल्कुल भी हैंडल का समर्थन नहीं करता है)।
आकार की सरणियाँ
HIDL संरचनाओं में आकार के सरणियों के लिए, उनके तत्व किसी भी प्रकार के हो सकते हैं जिनमें संरचना शामिल हो सकती है:
struct foo { uint32_t[3] x; // array is contained in foo };
स्ट्रिंग्स
C++ और Java में स्ट्रिंग्स अलग-अलग दिखाई देती हैं, लेकिन अंतर्निहित ट्रांसपोर्ट स्टोरेज प्रकार C++ संरचना है। विवरण के लिए, HIDL C++ डेटा प्रकार या HIDL जावा डेटा प्रकार देखें।
ध्यान दें: HIDL इंटरफ़ेस (जावा से जावा सहित) के माध्यम से जावा में या उससे स्ट्रिंग पास करने से वर्ण सेट रूपांतरण होंगे जो मूल एन्कोडिंग को सटीक रूप से संरक्षित नहीं कर सकते हैं।
vec<T> प्रकार का टेम्पलेट
vec<T>
टेम्प्लेट एक चर-आकार के बफर का प्रतिनिधित्व करता है जिसमें T
के उदाहरण होते हैं।
T
निम्नलिखित में से एक हो सकता है:
- आदिम प्रकार (जैसे uint32_t)
- स्ट्रिंग्स
- उपयोक्ता-परिभाषित गणनाएँ
- उपयोगकर्ता-परिभाषित संरचनाएँ
- इंटरफ़ेस, या
interface
कीवर्ड (vec<IFoo>
,vec<interface>
केवल शीर्ष-स्तरीय पैरामीटर के रूप में समर्थित है) - हैंडल
- बिटफील्ड<यू>
- vec<U>, जहां इंटरफ़ेस को छोड़कर U इस सूची में है (उदाहरण के लिए
vec<vec<IFoo>>
समर्थित नहीं है) - यू[] (यू के आकार की सरणी), जहां इंटरफ़ेस को छोड़कर यू इस सूची में है
उपयोगकर्ता-परिभाषित प्रकार
यह अनुभाग उपयोगकर्ता-परिभाषित प्रकारों का वर्णन करता है।
Enum
HIDL गुमनाम गणनाओं का समर्थन नहीं करता। अन्यथा, HIDL में एनम C++11 के समान हैं:
enum name : type { enumerator , enumerator = constexpr , … }
एक बेस एनम को HIDL में पूर्णांक प्रकारों में से एक के संदर्भ में परिभाषित किया गया है। यदि पूर्णांक प्रकार के आधार पर किसी एनम के पहले एन्यूमरेटर के लिए कोई मान निर्दिष्ट नहीं किया जाता है, तो मान डिफ़ॉल्ट रूप से 0 हो जाता है। यदि बाद के एन्यूमरेटर के लिए कोई मान निर्दिष्ट नहीं किया जाता है, तो मान पिछले मान प्लस वन पर डिफॉल्ट हो जाता है। उदाहरण के लिए:
// RED == 0 // BLUE == 4 (GREEN + 1) enum Color : uint32_t { RED, GREEN = 3, BLUE }
एक एनम पहले से परिभाषित एनम से भी विरासत में मिल सकता है। यदि चाइल्ड एनम के पहले एन्यूमरेटर के लिए कोई मान निर्दिष्ट नहीं किया गया है (इस मामले में FullSpectrumColor
), तो यह मूल एनम प्लस वन के अंतिम एन्यूमरेटर के मान पर डिफ़ॉल्ट होता है। उदाहरण के लिए:
// ULTRAVIOLET == 5 (Color:BLUE + 1) enum FullSpectrumColor : Color { ULTRAVIOLET }
चेतावनी: एनम इनहेरिटेंस अधिकांश अन्य प्रकार के इनहेरिटेंस से पीछे की ओर काम करता है। चाइल्ड एनम मान का उपयोग मूल एनम मान के रूप में नहीं किया जा सकता है। ऐसा इसलिए है क्योंकि एक बच्चे की सूची में माता-पिता की तुलना में अधिक मूल्य शामिल होते हैं। हालाँकि, पैरेंट एनम वैल्यू को चाइल्ड एनम वैल्यू के रूप में सुरक्षित रूप से उपयोग किया जा सकता है क्योंकि चाइल्ड एनम वैल्यू परिभाषा के अनुसार पैरेंट एनम वैल्यू का सुपरसेट है। इंटरफ़ेस डिज़ाइन करते समय इसे ध्यान में रखें क्योंकि इसका मतलब है कि पैरेंट एनम को संदर्भित करने वाले प्रकार आपके इंटरफ़ेस के बाद के पुनरावृत्तियों में चाइल्ड एनम को संदर्भित नहीं कर सकते हैं।
एनम के मानों को कोलन सिंटैक्स के साथ संदर्भित किया जाता है (नेस्टेड प्रकारों के रूप में डॉट सिंटैक्स नहीं)। सिंटैक्स Type:VALUE_NAME
है। यदि मान समान एनम प्रकार या चाइल्ड प्रकारों में संदर्भित है तो प्रकार निर्दिष्ट करने की आवश्यकता नहीं है। उदाहरण:
enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 }; enum Color : Grayscale { RED = WHITE + 1 }; enum Unrelated : uint32_t { FOO = Color:RED + 1 };
एंड्रॉइड 10 से शुरू होकर, एनम में एक len
विशेषता होती है जिसका उपयोग निरंतर अभिव्यक्तियों में किया जा सकता है। MyEnum::len
उस गणना में प्रविष्टियों की कुल संख्या है। यह मानों की कुल संख्या से भिन्न है, जो मान डुप्लिकेट होने पर छोटी हो सकती है।
struct
एचआईडीएल अज्ञात संरचनाओं का समर्थन नहीं करता है। अन्यथा, HIDL में संरचनाएँ C के समान हैं।
HIDL एक संरचना के भीतर पूरी तरह से निहित चर-लंबाई डेटा संरचनाओं का समर्थन नहीं करता है। इसमें अनिश्चित-लंबाई वाली सरणी शामिल है जिसे कभी-कभी C/C++ में किसी संरचना के अंतिम फ़ील्ड के रूप में उपयोग किया जाता है (कभी-कभी [0]
के आकार के साथ देखा जाता है)। HIDL vec<T>
एक अलग बफर में संग्रहीत डेटा के साथ गतिशील रूप से आकार वाले सरणी का प्रतिनिधित्व करता है; ऐसे उदाहरणों को struct
में vec<T>
के उदाहरण के साथ दर्शाया जाता है।
इसी तरह, string
एक struct
में समाहित किया जा सकता है (संबद्ध बफ़र्स अलग हैं)। जेनरेट किए गए C++ में, HIDL हैंडल प्रकार के उदाहरणों को एक पॉइंटर के माध्यम से वास्तविक मूल हैंडल पर दर्शाया जाता है क्योंकि अंतर्निहित डेटा प्रकार के उदाहरण परिवर्तनीय-लंबाई वाले होते हैं।
मिलन
एचआईडीएल अज्ञात यूनियनों का समर्थन नहीं करता है। अन्यथा, यूनियनें सी के समान हैं।
यूनियनों में फिक्स-अप प्रकार (पॉइंटर्स, फ़ाइल डिस्क्रिप्टर, बाइंडर ऑब्जेक्ट इत्यादि) नहीं हो सकते। उन्हें विशेष फ़ील्ड या संबंधित प्रकारों की आवश्यकता नहीं होती है और उन्हें केवल memcpy()
या समकक्ष के माध्यम से कॉपी किया जाता है। किसी यूनियन में सीधे तौर पर कुछ भी शामिल नहीं हो सकता है (या अन्य डेटा संरचनाओं के माध्यम से शामिल हो सकता है) जिसके लिए बाइंडर ऑफसेट (यानी, हैंडल या बाइंडर-इंटरफ़ेस संदर्भ) सेट करने की आवश्यकता होती है। उदाहरण के लिए:
union UnionType { uint32_t a; // vec<uint32_t> r; // Error: can't contain a vec<T> uint8_t b;1 }; fun8(UnionType info); // Legal
यूनियनों को संरचनाओं के अंदर भी घोषित किया जा सकता है। उदाहरण के लिए:
struct MyStruct { union MyUnion { uint32_t a; uint8_t b; }; // declares type but not member union MyUnion2 { uint32_t a; uint8_t b; } data; // declares type but not member }