2012-11-07 18 views
15

के बीच चर साझा करना मैं सी # में एक सॉफ्टवेयर लिख रहा हूं जिसे कई बार कॉल करने की आवश्यकता है और कई धागे एक सी ++ अप्रबंधित डीएल में एक फ़ंक्शन द्वारा कॉल करने की आवश्यकता है।सी # और सी ++

// "variables" which consist in some simple variables (int, double) 
// and in some complex variables (structs containing arrays of structs) 


extern "C" 
{ 
    __declspec(dllexport) int function1() 
    { 
     // some work depending on random and on the "variables" 
    } 
} 

और उस

public class class1 
{ 
    // "variables" <--- the "same" as the C++ file's ones 
    // Dll import <--- ok 

    public void method1() 
    { 
     int [] result; 

     for(int i=0; i<many_times; i++) 
     { 
      result = new int[number_of_parallel_tasks];     

      Parallel.For(0, number_of_parallel_tasks, delegate(int j) 
      { 
       // I would like to do result[j] = function1() 
      }); 

      // choose best result 
      // then update "variables" 
     } 
    } 

} 

मैंने लिखा "मैं करना चाहते हैं ..." C++ समारोह में जरूरत है क्योंकि जैसे एक सी # वर्ग:

मैं एक सी ++ ऐसे ही फ़ाइल है प्रत्येक दौर में "चर" अद्यतन भी है।

मेरा प्रश्न है:

यह आदेश संदर्भ हर बार गुजर से बचने के लिए सी ++ और सी # के बीच स्मृति साझा करने के लिए संभव है? क्या यह सिर्फ समय बर्बाद है?

मै मैनी मैप की गई फ़ाइलों के बारे में पढ़ता हूं। क्या वे मेरी मदद कर सकते हैं? हालांकि, क्या आप अधिक उपयुक्त समाधान जानते हैं?
बहुत बहुत धन्यवाद।

+1

मैंने आपके प्रश्न को ऊपर उठाया लेकिन आपको वास्तव में एक अच्छा उत्तर –

+0

प्राप्त करने के लिए "चर" की प्रकृति निर्दिष्ट करनी चाहिए। धन्यवाद। – 888

उत्तर

22

पी/Invoke का उपयोग करके सी # और सी ++ के बीच स्मृति साझा करने में कोई समस्या नहीं है पता है कि यह कैसे काम करता है। मैं एमएसडीएन में मार्शलिंग के बारे में पढ़ने का सुझाव दूंगा। आप असुरक्षित कीवर्ड और स्मृति को ठीक करने के बारे में भी पढ़ना चाहेंगे।

#pragma pack(1) 
typedef struct VARIABLES 
{ 
/* 
Use simple variables, avoid pointers 
If you need to use arrays use fixed size ones 
*/ 
}variables_t; 
#pragma pack() 
extern "C" 
{ 
    __declspec(dllexport) int function1(void * variables) 
    { 
     // some work depending on random and on the "variables" 
    } 
} 

सी # में कुछ इस तरह करते हैं:

सी में इस प्रकार ++ अपने समारोह की घोषणा:

यहां एक नमूना मानता है कि कि आपके चर एक सरल struct के रूप में वर्णित किया जा सकता है

[StructLayout(LayoutKind.Sequential, Pack=1)] 
struct variables_t 
{ 
/* 
Place the exact same definition as in C++ 
remember that long long in c++ is long in c# 
use MarshalAs for fixed size arrays 
*/ 
}; 

[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern int function(ref variables_t variables); 

और अपनी कक्षा में:

variables_t variables = new variables_t(); 
//Initialize variables here 
for(int i=0; i<many_times; i++) 
{ 
    int[] result = new int[number_of_parallel_tasks]; 
    Parallel.For(0, number_of_parallel_tasks, delegate(int j) 
    { 
      result[j] = function1(ref variables) 
    }); 

    // choose best result 
    // then update "variables" 
} 

आप अधिक जटिल परिदृश्यों का उपयोग कर सकते हैं जैसे C++ में संरचना को आवंटित करना और रिलीज़ करना, मार्शल के अन्य रूपों का उपयोग करके डेटा को वापस प्राप्त करने के लिए अपनी खुद की कक्षा बनाने और सीधे अप्रबंधित स्मृति को लिखने के लिए डेटा प्राप्त करने के लिए। लेकिन यदि आप अपने चर को पकड़ने के लिए एक सरल संरचना का उपयोग कर सकते हैं तो ऊपर की विधि सबसे सरल है।

संपादित करें: सही ढंग से और अधिक जटिल डेटा

तो नमूना ऊपर सही तरह से मेरी राय में है संभाल करने के लिए कैसे के लिए संकेतों के सी # और C के बीच "साझा" डेटा ++ अगर यह सरल डेटा जैसे है। आदिम प्रकारों या आदिम प्रकार के निश्चित आकार के सरणी धारण करने वाली संरचना।

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

उपर्युक्त मामले में मैं तर्क को आगे बढ़ाने की सिफारिश करता हूं कि सी ++ में "चर" को कैसे अपडेट किया जाए। C++ एक समारोह जोड़े कुछ इस तरह देखने के लिए:

extern "C" 
{ 
    __declspec(dllexport) void updateVariables(int bestResult) 
    { 
     // update the variables 
    } 
} 

मैं अभी भी वैश्विक चर का उपयोग करें ताकि मैं निम्नलिखित योजना का प्रस्ताव नहीं सुझाव है। C++ में:

typedef struct MYVERYCOMPLEXDATA 
{ 
/* 
Some very complex data structure 
*/ 
}variables_t; 
extern "C" 
{ 
    __declspec(dllexport) variables_t * AllocVariables() 
    { 
     // Alloc the variables; 
    } 
    __declspec(dllexport) void ReleaseVariables(variables_t * variables) 
    { 
     // Free the variables; 
    } 
    __declspec(dllexport) int function1(variables_t const * variables) 
    { 
     // Do some work depending on variables; 
    } 
    __declspec(dllexport) void updateVariables(variables_t * variables, int bestResult) 
    { 
     // update the variables 
    } 
}; 

सी # में: एक कक्षा बनाएं धारण करने के लिए स्मृति लौटे:

[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern IntPtr AllocVariables(); 
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern void ReleaseVariables(IntPtr variables); 
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern int function1(IntPtr variables); 
[DllExport("YourDll.dll", CallingConvention=CallingConvention.Cdecl)] 
static extern void updateVariables(IntPtr variables, int bestResult); 

आप की तरह निम्नलिखित कुछ करना होगा यदि आप अभी भी आप सी # में तर्क बनाए रखने के लिए चाहते हैं सी ++ से और अपनी मेमोरी एक्सेस लॉजिक लिखें। प्रतिलिपि semantics का उपयोग कर डेटा # सी का पर्दाफाश करें। सी # में

#pragma pack(1) 
typedef struct SUBSTRUCT 
{ 
int subInt; 
double subDouble; 
}subvar_t; 
typedef struct COMPLEXDATA 
{ 
int int0; 
double double0; 
int subdata_length; 
subvar_t * subdata; 
}variables_t; 
#pragma pack() 

आप इस

[DllImport("kernel32.dll")] 
static extern void CopyMemory(IntPtr dst, IntPtr src, uint size); 

[StructLayout((LayoutKind.Sequential, Pack=1)] 
struct variable_t 
{  
    public int int0; 
    public double double0; 
    public int subdata_length; 
    private IntPtr subdata; 
    public SubData[] subdata 
    { 
     get 
     { 
      SubData[] ret = new SubData[subdata_length]; 
      GCHandle gcH = GCHandle.Alloc(ret, GCHandleType.Pinned); 
      CopyMemory(gcH.AddrOfPinnedObject(), subdata, (uint)Marshal.SizeOf(typeof(SubData))*subdata_length); 
      gcH.Free(); 
      return ret; 
     } 
     set 
     { 
      if(value == null || value.Length == 0) 
      { 
       subdata_length = 0; 
       subdata = IntPtr.Zero; 
      }else 
      { 
       GCHandle gcH = GCHandle.Alloc(value, GCHandleType.Pinned); 
       subdata_length = value.Length; 
       if(subdata != IntPtr.Zero) 
        Marshal.FreeHGlobal(subdata); 
       subdata = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SubData))*subdata_length); 
       CopyMemory(subdata, gcH.AddrOfPinnedObject(),(uint)Marshal.SizeOf(typeof(SubData))*subdata_length); 
       gcH.Free(); 
      } 
     } 
    } 
}; 
[StructLayout((LayoutKind.Sequential, Pack=1)] 
sturct SubData 
{ 
    public int subInt; 
    public double subDouble; 
}; 

की तरह कुछ करने के ऊपर नमूना संरचना अभी भी रूप में पारित किया जा सकता है: क्या मेरा मतलब है इस प्रकार है, कहो तुम सी में है ++ इस तरह एक संरचना है पहले नमूने में। यह निश्चित रूप से संरचनाओं के सरणी के भीतर संरचनाओं और संरचनाओं के सरणी के साथ जटिल डेटा को संभालने के तरीके पर एक रूपरेखा है। जैसा कि आप देख सकते हैं कि आपको स्मृति भ्रष्टाचार से खुद को बचाने के लिए बहुत सारी प्रतिलिपि की आवश्यकता होगी। अगर सी ++ के माध्यम से स्मृति आवंटित की जाती है तो यदि आप इसे मुक्त करने के लिए FreeHGlobal का उपयोग करते हैं तो यह बहुत खराब होगा। यदि आप स्मृति की प्रतिलिपि से बचना चाहते हैं और फिर भी सी # के भीतर तर्क को बनाए रखना चाहते हैं तो आप जो कुछ भी चाहते हैं उसके लिए एक्सेसर्स के साथ एक देशी मेमोरी रैपर लिख सकते हैं उदाहरण के लिए आपके पास एनएच सरणी सदस्य के उप-निंट को सीधे सेट करने या प्राप्त करने का तरीका होगा - यह जिस तरह से आप अपनी प्रतियों को वैसे ही रखेंगे जो आप एक्सेस करते हैं।

एक और विकल्प विशिष्ट सी ++ कार्यों को आपके लिए कठिन डेटा हैंडलिंग करने के लिए लिखना होगा और उन्हें अपने तर्क के अनुसार सी # से कॉल करना होगा।

और अंतिम लेकिन कम से कम आप सीएलआई इंटरफेस के साथ हमेशा सी ++ का उपयोग नहीं कर सकते हैं। हालांकि मैं खुद ही ऐसा करता हूं अगर मुझे जरूरी है - मुझे लिंगो पसंद नहीं है लेकिन बहुत जटिल डेटा के लिए आपको निश्चित रूप से इसे विचार करना होगा।

संपादित

मैं पूर्णता के लिए DllImport करने के लिए सही बुला सम्मेलन गयी। कृपया ध्यान दें कि DllImport विशेषता द्वारा उपयोग किया जाने वाला डिफ़ॉल्ट कॉलिंग सम्मेलन Winapi (जो विंडोज़ पर __stdcall में अनुवाद करता है) जबकि सी/सी ++ में डिफ़ॉल्ट कॉलिंग कन्वेंशन (जब तक आप कंपाइलर विकल्प नहीं बदलते) __cdecl है।

+0

इस स्पष्ट उत्तर के लिए धन्यवाद। मैंने इसे उखाड़ फेंक दिया, लेकिन मैं अभी भी सशक्त नहीं हूं। शायद मेरे प्रश्न में मुझे यह निर्दिष्ट करने से चूक गया कि मैंने पहले ही पी/इनवॉक के साथ प्रयास किया है, लेकिन इस तरह (यदि मैं गलत नहीं हूं) तो मुझे प्रत्येक पुनरावृत्ति पर सभी चर (ठीक है, संदर्भ) पास करना होगा। मैं सोच रहा था कि इससे बचने का कोई तरीका है या नहीं। – 888

+1

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

3

सबसे अच्छी बात यह है कि आप अपने सी ++ कोड को परिभाषित कर सकते हैं (जिसे मैं मानता हूं अप्रबंधित)। फिर सी ++/सीएलआई में इसके लिए एक रैपर लिखें। रैपर आपको सी # के लिए एक इंटरफ़ेस प्रदान करेगा और वह स्थान है जहां आप मार्चिंग (अप्रबंधित से प्रबंधित क्षेत्र से डेटा ले जा रहे हैं)

+1

रैपर बनाने के लिए वे सी ++/सीएलआई का उपयोग क्यों करेंगे? आप आसानी से सी # में रैपर लिख सकते हैं। –

+1

सी ++/सीएलआई सभी विवरणों को संभालने में थोड़ा अधिक लचीला है, विशेष रूप से यदि आप प्रबंधित और अप्रबंधित कोड के बीच स्विच करना चाहते हैं। सी ++/सीएलआई, .NET और C++ का संयोजन, यदि आप कम से कम C++ (लेकिन यह केवल मेरी राय है) का उपयोग नहीं किया जाता है, तो 'आसान जा रहा' नहीं है। फिर सी # पर चिपके रहना एक बुद्धिमान विकल्प हो सकता है। – PapaAtHome

+0

मैंने खुद को बेहतर समझाने के लिए थोड़ा सा प्रश्न बदल दिया। – 888

1

चर/डेटा दोनों के संदर्भ को पारित करने से बचने के लिए एक तरीका है कि सी # और सी ++ कोड दोनों को देशी डीएलएल से दो कार्यों को निर्यात करना है। आपके फ़ंक्शन के अलावा जो काम करता है, एक और फ़ंक्शन प्रदान करता है जो संदर्भ को पारित करने और फ़ाइल स्कोप स्थिर पॉइंटर में संग्रहीत करने की अनुमति देता है जो उसी .cpp फ़ाइल में परिभाषित किया गया है, दोनों कार्यों के रूप में जो दोनों के लिए सुलभ है।

जैसा कि आपने बताया है कि आप मेमोरी मैप किए गए फ़ाइल का भी उपयोग कर सकते हैं (इस मामले में यह तब तक जारी नहीं रह सकता क्योंकि इसे डिस्क पर कभी भी लिखा जाना आवश्यक नहीं है)।