ऑब्जेक्ट-ओरिएंटेड C ++ कोड के लिए C रैपर एपीआई विकसित करना


81

मैं C APIs का एक सेट विकसित करना चाह रहा हूं जो हमारे मुख्य तर्क (ऑब्जेक्ट-ओरिएंटेड C ++ में लिखा गया) तक पहुंचने के लिए हमारे मौजूदा C ++ API के चारों ओर लपेटेगा। यह अनिवार्य रूप से एक गोंद एपीआई होगा जो हमारे C ++ तर्क को अन्य भाषाओं द्वारा प्रयोग करने योग्य बनाता है। कुछ अच्छे ट्यूटोरियल, किताबें, या सर्वोत्तम-प्रथाएं क्या हैं जो ऑब्जेक्ट-ओरिएंटेड C ++ के आसपास C लपेटने में शामिल अवधारणाओं को पेश करती हैं?


4
प्रेरणा के लिए zeromq स्रोत की जाँच करें। लाइब्रेरी वर्तमान में C ++ में लिखी गई है, और इसमें C बाइंडिंग है। zeromq.org
हसन सैयद

1
संबंधित (या यहां तक ​​कि एक डुप्लिकेट): सी खपत के लिए सी ++ वर्ग एपीआई लपेटना
उपयोगकर्ता

जवाबों:


70

यह हाथ से करना बहुत कठिन नहीं है, लेकिन यह आपके इंटरफ़ेस के आकार पर निर्भर करेगा। जिन मामलों में मैंने यह किया है वे शुद्ध सी कोड के भीतर से हमारी सी ++ लाइब्रेरी के उपयोग को सक्षम करने के लिए थे, और इस तरह एसडब्ल्यूआईजी को बहुत मदद नहीं मिली। (वैसे शायद SWIG का उपयोग यह करने के लिए किया जा सकता है, लेकिन मैं कोई SWIG गुरु नहीं हूँ और यह गैर-तुच्छ लग रहा था)

हम सब समाप्त कर रहे थे:

  1. हर वस्तु सी के बारे में एक अपारदर्शी संभाल में पारित किया है।
  2. कंस्ट्रक्टर और डिस्ट्रक्टर्स शुद्ध कार्यों में लिपटे हुए हैं
  3. सदस्य कार्य शुद्ध कार्य हैं।
  4. अन्य बिल्डरों को सी समकक्षों के लिए मैप किया जाता है जहां संभव हो।

तो इस तरह एक वर्ग (C ++ हैडर)

class MyClass
{
  public:
  explicit MyClass( std::string & s );
  ~MyClass();
  int doSomething( int j );
}

इस तरह (सी हैडर) के लिए एक सी इंटरफेस का नक्शा होगा:

struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );

इंटरफ़ेस का कार्यान्वयन इस तरह दिखाई देगा (C ++ स्रोत)

#include "MyClass.h"

extern "C" 
{
  HMyClass * myStruct_create( const char * s )
  {
    return reinterpret_cast<HMyClass*>( new MyClass( s ) );
  }
  void myStruct_destroy( HMyClass * v )
  {
    delete reinterpret_cast<MyClass*>(v);
  }
  int myStruct_doSomething( HMyClass * v, int i )
  {
    return reinterpret_cast<MyClass*>(v)->doSomething(i);
  }
}

हम किसी भी कास्टिंग की आवश्यकता से बचने के लिए मूल श्रेणी से अपने अपारदर्शी हैंडल को प्राप्त करते हैं, और (यह मेरी वर्तमान योग्यता के साथ काम नहीं करता था)। हमें हैंडल को एक स्ट्रक्चर बनाना होगा क्योंकि C क्लास को सपोर्ट नहीं करता है।

तो यह हमें मूल C इंटरफ़ेस देता है। यदि आप एक और पूर्ण उदाहरण दिखाना चाहते हैं, जिसमें आप अपवाद हैंडलिंग को एकीकृत कर सकते हैं, तो आप मेरे कोड को github पर आज़मा सकते हैं: https://gist.github.com/mikeando/5394166

मज़ेदार हिस्सा अब यह सुनिश्चित कर रहा है कि आपको सभी आवश्यक C ++ लाइब्रेरीज़ आपको बड़ी लाइब्रेरी में सही तरीके से जुड़ी हुई मिलें। जीसीसी (या क्लैंग) के लिए इसका मतलब है कि सिर्फ जी ++ का उपयोग करके अंतिम लिंक चरण करना।


11
मैं आपको शून्य से कुछ और उपयोग करने की सलाह दूंगा, उदाहरण के लिए किसी अनाम संरचना के बजाय एक शून्य * लौटी हुई वस्तु के लिए। यह लौटे हैंडल के लिए कुछ प्रकार की सुरक्षा दे सकता है। इसके बारे में अधिक जानकारी के लिए stackoverflow.com/questions/839765/… देखें ।
लेजरलेन

3
मैं लेजरन के साथ सहमत हूं और उसी के अनुसार अपना कोड रिफ्लेक्ट किया है
माइकल एंडरसन

2
@ माइक वेलर नया और एक्सटर्नल "सी" ब्लॉक के अंदर डिलीट करना ठीक है। बाहरी "C" केवल नाम के नाम पर प्रभाव डालता है। C कंपाइलर कभी भी उस फाइल को नहीं देखता है, केवल हेडर है।
माइकल एंडरसन

2
मुझे सी में यह सब संकलित करने के लिए आवश्यक एक टंकण की याद आती है। अजीब टाइपकीफ संरचना फू फू; "हैक"। कोड अपडेट किया गया है
माइकल एंडरसन

5
@MichaelAnderson, आपके myStruct_destroyऔर myStruct_doSomethingकार्यों में दो टाइपो हैं । होना चाहिए reinterpret_cast<MyClass*>(v)
फायरगुरफिकु

17

मुझे लगता है कि माइकल एंडरसन का जवाब सही रास्ते पर है लेकिन मेरा दृष्टिकोण अलग होगा। आपको एक अतिरिक्त चीज़ के बारे में चिंता करनी होगी: अपवाद। अपवाद C ABI का हिस्सा नहीं हैं, इसलिए आप अपवादों को कभी भी C ++ कोड से बाहर नहीं फेंक सकते। तो आपका हैडर ऐसा दिखने वाला है:

#ifdef __cplusplus
extern "C"
{
#endif
    void * myStruct_create( const char * s );
    void myStruct_destroy( void * v );
    int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif

और आपकी आवरण की .cpp फ़ाइल इस तरह दिखाई देगी:

void * myStruct_create( const char * s ) {
    MyStruct * ms = NULL;
    try { /* The constructor for std::string may throw */
        ms = new MyStruct(s);
    } catch (...) {}
    return static_cast<void*>( ms );
}

void myStruct_destroy( void * v ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    delete ms;
}

int myStruct_doSomething( void * v, int i ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    int ret_value = -1; /* Assuming that a negative value means error */
    try {
        ret_value = ms->doSomething(i);
    } catch (...) {}
    return ret_value;
}

इससे भी बेहतर: यदि आप जानते हैं कि आप सभी को MyStruct के एकल उदाहरण के रूप में आवश्यकता है, तो शून्य बिंदुओं से निपटने के जोखिम को अपने API पर न लें। इसके बजाय कुछ ऐसा करें:

static MyStruct * _ms = NULL;

int myStruct_create( const char * s ) {
    int ret_value = -1; /* error */
    try { /* The constructor for std::string may throw */
        _ms = new MyStruct(s);
        ret_value = 0; /* success */
    } catch (...) {}
    return ret_value;
}

void myStruct_destroy() {
    if (_ms != NULL) {
        delete _ms;
    }
}

int myStruct_doSomething( int i ) {
    int ret_value = -1; /* Assuming that a negative value means error */
    if (_ms != NULL) {
        try {
            ret_value = _ms->doSomething(i);
        } catch (...) {}
    }
    return ret_value;
}

यह API बहुत अधिक सुरक्षित है।

लेकिन, जैसा कि माइकल ने उल्लेख किया है, लिंकिंग बहुत मुश्किल हो सकता है।

उम्मीद है की यह मदद करेगा


2
इस मामले के लिए अपवाद से निपटने के बारे में अधिक जानकारी के लिए निम्नलिखित थ्रेड पर एक नज़र डालें: stackoverflow.com/questions/847279/…
Laserallan

2
जब मुझे पता है कि मेरी C ++ लाइब्रेरी में C API भी होगा, तो मैं अपने अपवाद बेस क्लास के अंदर API त्रुटि कोड int को एन्क्रिप्ट करता हूं। फेंकने वाली साइट पर यह जानना आसान है कि सटीक त्रुटि स्थिति क्या है और एक बहुत विशिष्ट त्रुटि कोड प्रदान करता है। बाहरी C API फ़ंक्शंस में ट्रैप-कैच "रैपर" को केवल त्रुटि कोड प्राप्त करने और इसे कॉलर को वापस करने की आवश्यकता होती है। अन्य मानक पुस्तकालय अपवादों के लिए, लेज़रलेन के लिंक को देखें।
एमिल कॉर्मियर

2
पकड़ (...) {} शुद्ध अनधिकृत बुराई है। मेरा एकमात्र खेद यह है कि मैं केवल एक बार वोट डाउन कर सकता हूं।
टेरी महाफ़ेई

2
@ टेरी महाफ़ी मैं आपसे पूरी तरह सहमत हूँ कि यह बुराई है। सबसे अच्छा वह है जो एमिल ने सुझाया है। लेकिन अगर आपको इस बात की गारंटी देनी चाहिए कि लिपटा हुआ कोड कभी नहीं फटेगा, तो आपके पास पहचान किए गए अन्य सभी कैच के निचले भाग पर कैच (...) लगाने के अलावा और कोई चारा नहीं है। यह मामला है क्योंकि आपके द्वारा लाई जा रही लाइब्रेरी खराब तरीके से प्रलेखित हो सकती है। कोई C ++ कंस्ट्रक्शन नहीं है जिसका उपयोग आप इस बात को लागू करने के लिए कर सकते हैं कि केवल अपवादों का एक सेट फेंका जा सकता है। दो बुराइयों से कम क्या है? जब लिपटे हुए कोड C कॉलर को फेंकने का प्रयास करते हैं, तो एक रन-टाइम क्रैश को पकड़ (...)?
आलंकारिक

1
पकड़ (...) {एसटीडी :: समाप्त (); } स्वीकार्य है। पकड़ (...) {} एक संभावित सुरक्षा छेद है
टेरी महफ़ेई

10

C ++ कोड को C से उजागर करना मुश्किल नहीं है, बस Facade डिजाइन पैटर्न का उपयोग करें

मैं मान रहा हूं कि आपका C ++ कोड एक लाइब्रेरी में बनाया गया है, आपको बस इतना करना है कि आप अपनी C ++ लाइब्रेरी में एक C मॉड्यूल को अपनी लाइब्रेरी के साथ-साथ एक शुद्ध C हैडर फ़ाइल के रूप में बनाएं। C मॉड्यूल प्रासंगिक C ++ फ़ंक्शन को कॉल करेगा

एक बार जब आप ऐसा कर लेते हैं कि आपके सी एप्लिकेशन और लाइब्रेरी में आपके द्वारा उजागर किए गए सी एपीआई तक पूरी पहुंच होगी।

उदाहरण के लिए, यहाँ एक नमूना मुखौटा मॉड्यूल है

#include <libInterface.h>
#include <objectedOrientedCppStuff.h>

int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
      Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
      obj->doStuff(arg2);
      return obj->doMoreStuff(arg1);
   }

आप तब इस सी फ़ंक्शन को अपने एपीआई के रूप में उजागर करते हैं और आप इसे चिंता के साथ सी काम के रूप में स्वतंत्र रूप से उपयोग कर सकते हैं

// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);

जाहिर है कि यह एक विरोधाभासी उदाहरण है लेकिन C ++ लाइब्रेरी को C के सामने लाने का यह सबसे आसान तरीका है


Hi @hhafez क्या आपके पास एक सरल हैलो वर्ल्ड उदाहरण है? एक तार के साथ?
जेसन फोगलिया

एक गैर cpp आदमी के लिए यह प्यारा है
निकल्स एवेन

6

मुझे लगता है कि आप दिशा पर कुछ विचार प्राप्त करने में सक्षम हो सकते हैं और / या संभवतः सीधे SWIG का उपयोग कर सकते हैं । मुझे लगता है कि कुछ उदाहरणों पर जाने से कम से कम आपको एक विचार मिलेगा कि एक एपीआई को दूसरे में लपेटते समय किस तरह की चीजों पर विचार करना चाहिए। व्यायाम फायदेमंद हो सकता है।

SWIG एक सॉफ्टवेयर डेवलपमेंट टूल है जो C और C ++ में लिखे गए प्रोग्राम्स को विभिन्न उच्च स्तरीय प्रोग्रामिंग भाषाओं के साथ जोड़ता है। SWIG का उपयोग विभिन्न प्रकार की भाषाओं के साथ किया जाता है, जिसमें सामान्य स्क्रिप्टिंग भाषाएं जैसे पर्ल, PHP, पायथन, Tcl और रूबी शामिल हैं। समर्थित भाषाओं की सूची में C #, कॉमन लिस्प (CLISP, Allegro CL, CFFI, UFFI), जावा, लुआ, मोडुला -3, OCAML, ऑक्टेव और R जैसी गैर-स्क्रिप्टिंग भाषाएं भी शामिल हैं। कई व्याख्यात्मक और संकलित योजना कार्यान्वयन ( गुइल, मेज़शेम, चिकन) का समर्थन किया जाता है। SWIG का उपयोग आमतौर पर उच्च स्तर की व्याख्या या संकलित प्रोग्रामिंग वातावरण, उपयोगकर्ता इंटरफेस बनाने के लिए किया जाता है, और C / C ++ सॉफ़्टवेयर के परीक्षण और प्रोटोटाइप के लिए एक उपकरण के रूप में। SWIG अपने पार्स ट्री को एक्सएमएल और लिस्प एस-एक्सप्रेशन के रूप में भी निर्यात कर सकता है। SWIG का स्वतंत्र रूप से उपयोग, वितरण किया जा सकता है,


2
SWIG बस खत्म हो गया है, अगर वह सब करना चाहता है एक सी ++ पुस्तकालय सी। से उपलब्ध होने के लिए है
hhafez

1
यह एक राय है और इसमें कोई वास्तविक उपयोगी प्रतिक्रिया नहीं है। यदि मूल कोड है, तो SWIG मदद करेगा: तेजी से बदलना, इसे बनाए रखने के लिए कोई C ++ संसाधन नहीं हैं और केवल C संसाधन उपलब्ध हैं और यदि डेवलपर C API पीढ़ी को स्वचालित करना चाहता है। ये SWIG का उपयोग करने के लिए सामान्य और निश्चित रूप से मान्य कारण हैं।
user1363990

5

बस एक वस्तु की अवधारणा को एक के साथ बदलें void *(अक्सर सी उन्मुख पुस्तकालयों में एक अपारदर्शी प्रकार के रूप में संदर्भित) और सी ++ से आप जो भी जानते हैं उसका पुन: उपयोग करें।


2

मुझे लगता है कि SWIG का उपयोग करना सबसे अच्छा जवाब है ... यह न केवल पहिए को फिर से लगाने से रोकता है बल्कि यह विश्वसनीय है और समस्या की शूटिंग के बजाय विकास में निरंतरता को बढ़ावा देता है।

उच्च आवृत्ति समस्याओं को दीर्घकालिक समाधान द्वारा संबोधित करने की आवश्यकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.