2015-09-15 9 views
5

चलो कहते हैं कि मैं सी में निम्नलिखित structs है ++सी # marshaling सी ++ struct विरासत

struct Base 
{ 
    USHORT size; 
} 

struct Inherited : public Base 
{ 
    BYTE type; 
} 

मैं सी # में Inherited मार्शल करने के लिए चाहते हैं, लेकिन struct विरासत सी # काम नहीं करता हूँ। निम्नलिखित उपयुक्त कर रहा है?

public interface IBase 
{ 
    ushort Size { get; set; } 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct Inherited : IBase 
{ 
    public ushort Size { get; set; } 
    public byte Type { get; set; } 
} 

मैंने यहां समस्या को सरल बना दिया और मेरे structs परिणाम को मान्य करना मुश्किल बना रहे हैं। साथ ही, structs किसी अन्य सॉफ़्टवेयर से आ रहे हैं जो इतना अच्छा दस्तावेज़ नहीं है जिससे परिणाम को सत्यापित करना मुश्किल हो जाता है। सी ++ में विरासत का उपयोग करते समय, बाल संरचना से पहले या उसके बाद बेस क्लास फ़ील्ड हैं?

मैं बेस फ़ील्ड को उपस्थित होने के लिए IBase का उपयोग कर रहा हूं।

दुर्भाग्यवश, मेरे पास सी ++ पक्ष (बाहरी प्रणाली के लिए एसडीके के साथ एकीकृत) पर नियंत्रण नहीं है।

+0

मुझे लगता है कि बेस स्ट्रक्चर को सी ++ मेमोरी लेआउट में पहले आने की अपेक्षा करना अनुचित नहीं है, जैसे कि यह पहला सदस्य था (लेकिन मुझे नहीं लगता कि यह मानकीकृत है)। या तुमने कोशिश की? –

+0

@TheodorosChatzigiannakis हां, मैंने कोशिश की और कोड चलाया लेकिन जैसा कि मैंने कहा: "[...] मेरे structs बड़े पैमाने पर परिणाम को मान्य करना मुश्किल बनाते हैं।" यही कारण है कि मैं अपने हाइपोटिस को प्रमाणित करने की कोशिश कर रहा हूं। आपके इनपुट के लिए धन्यवाद! – KOTIX

+0

आपको शायद गुणों के बजाय फ़ील्ड का उपयोग करना चाहिए, लेकिन मुझे नहीं पता कि यह काम करेगा या नहीं। –

उत्तर

2

शब्द "उचित" वास्तव में इन सी # घोषणाओं पर लागू नहीं होता है। दुर्घटनाओं से बचने के लिए अब तक का सबसे अच्छा तरीका गुणों और इंटरफेस के कार्यान्वयन विवरण पर निर्भर करता है। इस संरचना को आंतरिक घोषित किया जाना चाहिए और केवल सादे फ़ील्ड का उपयोग करें।

टुकड़ा तो मैं ग्रहण करने के लिए यह असली घोषणा करता है कि एक समस्या है का एक सरलीकृत संस्करण है कि होगा एक विफलता मोड प्रदर्शित नहीं करता है। यह जांचने का तरीका है कि सी # कोड संरचना घोषणा को सही बनाता है यह सत्यापित करना है कि संरचना का आकार और अंतिम फ़ील्ड का ऑफसेट सी ++ और सी # दोनों में समान है। कि जाँच करने के लिए एक छोटे से परीक्षण कार्यक्रम लिखकर प्रारंभ करें, यह स्निपेट के लिए सी ++ संस्करण इस तरह दिखना चाहिए:

#include <Windows.h> 
#include <stddef.h> 

struct Base { 
    USHORT size; 
}; 

struct Inherited : public Base { 
    BYTE type; 
}; 


int main() 
{ 
    int len = sizeof(Inherited); 
    int ofs = offsetof(Inherited, type); 
    return 0; 
} 

और लेन और ofs चर, 4 और इस में 2 निरीक्षण करने के लिए डिबगर का उपयोग मामला। सी #:

using System; 
using System.Runtime.InteropServices; 

class Program { 
    static void Main(string[] args) { 
     var len = Marshal.SizeOf(typeof(Inherited)); 
     var ofs = Marshal.OffsetOf(typeof(Inherited), "<Type>k__BackingField"); 
    } 
} 
public interface IBase { 
    ushort Size { get; set; } 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct Inherited : IBase { 
    public ushort Size { get; set; } 
    public byte Type { get; set; } 
} 

अभी भी 4 और 2, तो एकदम सही मिलान और पिनवोक अच्छा होना चाहिए। जब आप वास्तविक घोषणा पर मेल नहीं खाते हैं, तो वैरिएबल पर अपना रास्ता पीछे की ओर काम करें, आप उस सदस्य को खोज लेंगे जिसे गलत घोषित किया गया था। बैकिंग फ़ील्ड के गेटकी नाम पर जांच करने के लिए मजबूर होना, संपत्ति का उपयोग करने के परिणाम पर ध्यान दें। गुणों की बजाय फ़ील्ड का उपयोग करके संरचना घोषित होने पर यह कोड बहुत कम हो जाएगा, दृढ़ता से अनुशंसित।

1

आप इस बात की धारणा बना रहे हैं कि सी ++ कंपाइलर कक्षा में कक्षा को कैसे रखेगा। आपके कंपाइलर & के आधार पर आप किस झंडे का उपयोग कर रहे हैं, यह बदल सकता है। आपके द्वारा उपयोग किए जा रहे फ़ील्ड के आधार पर भी। उदाहरण के लिए, कुछ compilers इस तरह एक वस्तु संरेखित करने के लिए पूरी तरह से उचित होगा:

struct Obj 
{ 
    char c; // <-- starts at 0 byte 
    int i; // <-- starts at 4 byte - 4 byte alignment improves performance. 
} 

तो आप देख सकते हैं कि कैसे सी ++ कक्षाएं आप उन्हें कैसे उम्मीद सी # में बाहर रखी जा करने के लिए मैप नहीं हो सकता है।

इस पर नियंत्रण करने के लिए झंडे हैं - आप पैकिंग को C++ में 0 सेट कर सकते हैं, फिर सी # में अनुक्रमिक लेआउट का उपयोग करें, और फिर आपका दृष्टिकोण उचित है।

गुणों का उपयोग एक बड़ी समस्या नहीं है - जब तक आप समझते हैं कि जहां कंपाइलर द्वारा अंतर्निहित बैकिंग फ़ील्ड आपके लिए जेनरेट किया जा रहा है।

0

मुझे अंत में वास्तविक समस्या मिली जो मैं करने की कोशिश कर रहा था। मेरे द्वारा उपयोग किए जाने वाले एसडीके से कॉलबैक मुझे struct Base भेजता है और अंदर के क्षेत्रों का विश्लेषण करके मैं निर्धारित करता हूं कि यह किस प्रकार का विरासत है। तब मुझे मूल प्रकार को विरासत प्रकार में "कास्ट" करना होगा।

static T CopyStruct<T>(ref object s1) 
{ 
    GCHandle handle = GCHandle.Alloc(s1, GCHandleType.Pinned); 
    T typedStruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
    handle.Free(); 
    return typedStruct; 
} 

कर यह struct Base के आकार के बाहर जाना कभी नहीं होगा की इस तरह: यहाँ मैं शुरू में क्या कर रहा था है। इसलिए, Inherited प्रकार के सभी अतिरिक्त फ़ील्ड सही ढंग से प्रारंभ नहीं किए जाएंगे (कॉपी की गई स्मृति के बाहर पढ़ना)। मैं इस प्रकार एक असुरक्षित तरीके से यह कर समाप्त हो गया: मूल स्मृति ब्लॉक करने के लिए

fixed (Base* basePtr= &base) 
{ 
    inherited= *(Inherited*) basePtr; 
} 

इस तरह, inherited अंक और base आकार के बाहर पढ़ सकते हैं।

आपके पिछले सभी उत्तरों के लिए धन्यवाद! मैंने वास्तव में मेरे सी ++ मॉडल के आकार और ऑफसेट को मान्य करने के लिए एक सी ++ एप्लिकेशन बनाया था।

संबंधित मुद्दे